I am trying to devise a solution to rename a large collection of files so that the text in line 3 of each file is the new filename. These files all have the same structure.

These are HTML files and specifically line 3 of each file looks like this.


I want to grab everything between the single quotes and use that text to rename the file with that text as the filename.

Renaming these one at a time would useful.

  • Thanks for all the pointers!! - I will try these and come back with comments and mark the answers if I can get them to work. Feb 5, 2019 at 14:48

4 Answers 4

for src in *.html; do
  { read -r x && read -r x && IFS="'" read -r x dst x; } < "$src" &&
     mv -i -- "$src" "$dst.html"

(the -i is for interactive to give the user a chance to avoid losing files if two end up with the same destination name).

$ awk -v FS="'" -v OFS="\t" 'FNR==3 && NF>2 {print FILENAME, $2; nextfile}' *.txt |parallel --colsep "\t" 'mv {1} {2}'

awk iterate over all files in the location. We define the ' as the field separator. Whenever it reaches the third line and there are more than 2 fields (which should be if we have two ') it prints out the filename and the second field (which is the part between the first two ') delimited by a tab. Then it skipes to the next file.

The result is piped to parallel. parallel execute the mv command by replacing {1} and {2} with values given in the columns from the awk result.

Some notes:

  • nextfile is not available in all awkversions
  • whitespaces in a filename is never a good idea. You could replace them by an underscore if you change the awk command like this:

    awk -v FS="'" -v OFS="\t" 'FNR==3 && NF>2 { gsub(" ", "_", $2); print FILENAME, $2; nextfile}' *.txt

  • You should move or copy your new filenames to another folder. I'm not sure how awk will react if new files appear in the same folder during runtime.
for j in ./*.txt
i=$( sed -n '3p' "$j" | cut -d "'" -f2)
mv "$j" "$i"

It will search all the files in the current directory and will move this file to the new one.


I would do such a thing in perl instead of putting together some slow and fragile tangle of subshells:

perl -e 'while(<>){
  sub no_rename { print "rename @_\n" }
  next unless $. == 3;
  if(my ($f) = /DATA POPULATION +'\''(.*?)'\''/){
      $f =~ s/[^\w]/_/g;
      no_rename $ARGV, $f or warn "rename $ARGV, $f: $!\n";
  close ARGV
}' files ...

You'll have to change no_rename to rename to make it actually do it, instead of showing it.

Sorry for the ugly '\''; if you put that in a script file instead of one-liner, that should be simply:


If you really want to create filenames with spaces, also add a \s after the \w in the s///g expression (and similar for other characters that could go in the filename -- by default, the script will replace everything but letters and digits with an underscore).

  • I hate spaces in filenames too and never do it, but this is how this data was generated by a coworker who painted us into this corner. Feb 5, 2019 at 15:27
  • removing the $f =~ ... line will leave the filename as-is; though you should at least replace slashes, newlines and starting dots: s|[/\n]|_|g, s/^\./_/ for $f. Unless you like ... surprises.
    – user313992
    Feb 5, 2019 at 16:02

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .