1

So I have some file that has contents to be used and some input parameters in the file heading. I would like to change the inputs but keep the contents the same. I also name the files based on which input is being used. For example, let's say I have a file name that is:

input1-input2-input3

I would like to copy that file and change input3 to input4 or something like that, and I want the name to reflect this. I have been trying to write a simple bash script that looks like this:

#!/bin/bash
#Inputs:{file to be copied} {solvent to change}
file=${1?No file given}
input3=${2?no input}

do some changes

cp file newfile

The internet has given me some tips on that "do some changes part", like this tip: http://www.peteryu.ca/tutorials/shellscripting/batch_rename so my current attempt looks like this with a filename called

test-gs-xcf-solvent.gjf

 #!/bin/bash
 
 #Inputs:{file to be copied} {solvent to change} 
 file=${1?No file given} 
 solvent=${2?no solvent given}
 
 
 filename=$(basename "$file" .gjf) ls -1 $file  | awk '{print("mv "$1 "" $1)}' | sed "s/solvent/$solvent/2" > rename_"$filename".txt 
 newfile=awk '{print $3}' rename_"$filename".txt 
 if [ -f "$newfile" ] 
 then
         echo "$newfile" > "$dir/newfile" 
 fi 
 cp "$file" "$newfile"

~

This hasn't worked for me because I think that link is answering a different question.

In summary, I have a file called:

input1-input2-input3

I would like to write a bash script that would look like this:

.\bash.bash input1-input2-input3 input4

with the output being:

input1-input2-input4

In theory, I would like to able to change all of those fields in the future, but for now, I would like to just keep the example simpler.

Thank you all! This is my first post here, so please let me know if I have any formatting issues

3

Hi German Barcenas :) We have prepared a small script that does what you ask for at the end of the question:

The script

#!/bin/bash

# Get the input parameters
old_file=${1?No file given} 
solvent=${2?no solvent given}

# Get the basename of the file
old_file_base=$(basename $old_file '.gjf')

# Run it through awk
new_file=$(echo $old_file_base | awk -v sol=$solvent '{split($0, tmp, "-"); \
        tmp[3] = sol; print sprintf("%s-%s-%s", tmp[1], tmp[2], tmp[3])}')

# It can all be compressed into one line
# new_file=$(basename $old_file '.gjf' | awk -v sol=$solvent '{split($0, foo, "-"); \
          # foo[3] = sol; print sprintf("%s-%s-%s", foo[1], foo[2], foo[3])}')

# Work with the file...

# Uncomment to check the new filename is correct
echo $new_file

# And copy the file
# cp $old_file $new_file

Breaking it down

We have tried the script on bash 3.2.57 and it should run as is. It'll just output the filename that would be added to the newly copied file but you can tweak it as you see fit. Let's take a look at each piece.

  1. The input parameters
old_file=${1?No file given} 
solvent=${2?no solvent given}

This part is copied from your example so we won't go into much detail. We just want to point out we changed the name of the first input parameter to try and make the naming clearer.

  1. Getting the basename
old_file_base=$(basename $old_file '.gjf')

This part will save the base name of the file into the old_file_base variable. This extra step is not needed but we have added it to try and make everything clearer. We run this command to make the script work even if the provided path to the file is absolute (i.e. it's given from the root folder so it begins with /. An example of an absolute path is /absolute/path/to/foo).

  1. Running it through awk
new_file=$(echo $old_file_base | awk -v sol=$solvent '{split($0, tmp, "-"); \
        tmp[3] = sol; print sprintf("%s-%s-%s", tmp[1], tmp[2], tmp[3])}')

This is where the "heart" of the program lies. We'll start by using echo to output the contents of old_file_name to STDOUT and then using a pipe (|) to redirect echo's STDOUT to awk's STDIN. That way awk will act on the base name we got in the previous step.

Note that we are using option -v when calling awk. We do so to "pass" external variables to awk so that they can be used when processing the input. With -v sol=$solvent we can reference sol within awk and its value will be that of the solvent variable in the script (i.e. the second parameter passed when running the script). We then find the awk "script" between the curly braces. It contains 3 distinct instructions:

a) split($0, tmp, "-"): The split() function will take the entire input ($0 within awk) and split it on the - characters. The results will be returned in the tmp array.

b) tmp[3] = sol: We'll just assign sol to the third element. Please note awk begins indexing at 1 not 0! We are hardcoding the index because we know the format the input filename will have. Otherwise you might need to be careful here and check the array's size manually.

c) print sprintf("%s-%s-%s", tmp[1], tmp[2], tmp[3]): The sprintf() function will "return" the string we get by assigning the passed values to the placeholders defined with the percentage (%) sign. As in C, %s is a placeholder for a string. In this case sprintf() will return the three elements of the tmp array joined by hyphens (-) as a string. As we changed the value of tmp[3] with the desired output before, the result will be what we expect it to be. We then call print to output that string to STDOUT. As this entire command is within a command substitution structure (i.e. $()) the contents outputted to STDOUT will be assigned to the new_file variable.

We then have the desired output! You can check that by running echo $new_file and checking your terminal's output. If you pass a 3rd parameter on the command line to control what input to alter in the filename you could just pass it to awk through the -v option and process the name however you want within awk itself.

Sample outputs

These are some of the outputs we got on our machine:

bash-3.2$ ./foo.sh input1-input2-input3 input-5
input1-input2-input-5
bash-3.2$ ./foo.sh input1-input2-input3 input-4
input1-input2-input-4

We did name our script foo.sh, we are kind of bad with names :P

I hope this script helps you in any way. If I misunderstood your query in any way or if you need any further guidance please let me know so that I can lend a hand :)

2
  • This does what I need. Thank you so much! Nov 11 '20 at 0:44
  • @German, if you could mark the question as resolved and give it a +1 it would help other people that have the same issue :P I'm glad my help was useful :)
    – Collado
    Nov 11 '20 at 17:17

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.