Renaming files by appending string from within file to filename

Greetings. I am working in a Linux environment and am trying to figure out a way to rename files in a directory by appending a unique strings that appears within a certain area in those files. I have gotten as far as identifying what that particular unique string is with a command like the following:

for a in *split*; do cat $a | cut -d "~" -f 9 | head -n 1; done | wc -l

The part that I am trying to figure out is how to rename the various files in the directory by appending the value that the aforementioned command returns. For example, if filename "filename001," "filename002," and "filename009" contains strings "ABC," "DEF," and "XYZ" in the particular field respectively, the new filenames should read "filename001_ABC," "filename002_DEF," and "filename009_XYZ."

Thanks in advance for the assistance and please feel free to suggest elegant alternatives of the aforementioned command if you'd like.

Hi, try so

for i in *split*; do tail="$(head -1 $i |cut -d "~" -f 9 )"; mv $i ${i}_$tail; done
1 Like

Hi, try something like this:

for file in filename*                                  # For every file in the current directory that starts with "filename"
do
  if [ -r "$file" ]; then                              # Check if the file is readable
    IFS='~' read x x x x x x x x string x < "$file"    # Read the 9th field of the first line of the file into variable string and close
    if [ "$string" ]; then                             # Check if the string is not empty
      mv -- "$file" "${file}_${string}"                # Rename the file 
    fi 
  fi
done

you can put an echo statement before the mv command first to see what it does

--
If you are using bash , you can also read the fields of the first line into an array:

IFS='~' read -a string <"$file"

and then use ${string[8]} for the ninth field.

3 Likes

But first check

for i in *split*; do tail="$(head -1 $i |cut -d "~" -f 9 )"; echo $i ${i}_$tail; done

Thanks so much. I tried both methods and they worked. Unfortunately, I forgot to take into account that the particular field within the file where the string is extracted from the space(s) that follow it and both methods factored them in when renaming the files. Using the example filenames from before as an example, the renamed filenames read " "filename001_ABC ," " filename002_DEF ," and " filename009_XYZ " (the number of empty spaces varies). The ultimate goal is to have the filenames renamed as mentioned before but without the spaces and concluding with a specific suffix. I have tried variations of

for f in filename*; do mv "$f" `echo $f | tr ' ' '_'`; done

like

for a in filename*; do mv $a $a.edi; done

but they have been no-gos so far.

In bash, instead of ${string} you can use ${string// } to remove all spaces from string .

Another option is to use read again (bash/ksh93/zsh, but this time with the default IFS (Internal Field Separator)):

read string2 <<< "$string"

or other shells:

read string2 << EOF
$string
EOF
1 Like

I'm not sure what you're trying to do. If you accidentally created a bunch of files with trailing <space> characters in their names and you just want to get rid of the trailing <space>s, try the following:

for file in *' '
do	mv "$file" $file
done

but this won't work if there are any <space> characters in the middle of a file's name.

@Don, but that may give unexpected results if the original field 9 contained wildcard characters, no?

Yes. But for the three filenames shown in post #5:

it works (assuming that the leading <double-quote> characters before " filename001_ABC " inside the ICODE tags was a typo).

Thank you so much for this tidbit. I modified what should be line number nine in the excerpt below which eliminated two lines of code that I came across elsewhere. Below is an excerpt of the shell script file that includes your code which seems to work as expected and hoped.
-

for a in $(ls *.*); do csplit -s -k -f "$a"_split $a /^ISA/ {*}; rm *split00; mv *split[0-9]* split/; mv $a original;done
cd split
for a in $(ls *split*); do sed ':a;N;$! ba;s/\n//g' < $a > "$a"_$(TZ=":US/Eastern" date -r $a +%Y%m%d_%H%M%SET); rm $a; done
for file in *split*
do
  if [ -r "$file" ]; then
#    IFS='~' read -a ${string[8]} < "$file"
    IFS='~' read x x x x x x x x string x < "$file"
    if [ "$string" ]; then
      mv -- "$file" "${file}_${string// }.edi"
     fi
  fi
done

Thank you again.

By the way, I wasn't able to get what you mentioned the following earlier but I imagine that I was implementing it incorrectly which resulted in the errors when I tried to tinker with it. If you don't mind, can you walk me through implementing the cleaner-looking method using the array, please?
-

What shell version do you use?
What "shebang" in the beginning of your script?
What did you do?
Which errors did you get?
What be the output of

IFS='~' read -a string <"$file"
echo ${string[8]}