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. – Daniel Cross Feb 5 '19 at 14:48
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).

| improve this answer | |
$ 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.
| improve this answer | |
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.

| improve this answer | |

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).

| improve this answer | |
  • 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. – Daniel Cross Feb 5 '19 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. – pizdelect Feb 5 '19 at 16:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.