0

I have a list of files in bash - file1.txt, file2.txt, file3.txt and I would like to make another list that include this strings without .txt, so

names2 = (file1, file2, file3)

Then, I would like to find these strings in a file and add a before this strings. How to do that please?

My Code:

names = (file1.txt, file2.txt, file3.txt)

for i in "${names[@]}"; do
    awk '{ gsub("$i","a-$i") }' f.txt > g.txt

f.txt:

TEXT
\connect{file1}
\begin{file2}
\connect{file3}
TEXT 
75

Desired output g.txt

TEXT
\connect{a-file1}
\begin{a-file2}
\connect{a-file3}
TEXT 
75
Kiran Mistry
  • 2,614
  • 3
  • 12
  • 28
Archie
  • 93
  • 2
  • 9
  • You can not use $i directly in `awk`, read this on how to use variables with `awk` https://stackoverflow.com/questions/19075671/how-do-i-use-shell-variables-in-an-awk-script – Jotne Nov 01 '19 at 05:53
  • 1
    `names2 = (file1, file2, file3)` is not a valid declaration of an array. There are no spaces in bash assignments. `names=(file1.txt, file2.txt, file3.txt)` is the right approach – Inian Nov 01 '19 at 05:53
  • Add a shebang and then paste your script there: http://www.shellcheck.net/ – Cyrus Nov 01 '19 at 06:05
  • There's no reason to put the strings in an array if you only use the array once. Just say `for i in file1 file2 file3; do ...` – tripleee Nov 01 '19 at 10:13

2 Answers2

2

Could you please try following.

Variable with values in shell:

string="file1.txt, file2.txt, file3.txt"

Creating a shell array as follows:

IFS=', ' read -r -a array <<< "$string"

OR if you want to stick with your way of defining array then do like:

array=(file1.txt file2.txt file3.txt)

Passing above shell created array to awk and reading Input_file for doing the final operations.

awk -v arr="${array[*]}" '
BEGIN{
  FS=OFS="{"
  num=split(arr,array," ")
  for(i=1;i<=num;i++){
    sub(/\.txt/,"",array[i])
    array1[array[i]"}"]
  }
}
$2 in array1{
  $2="a-"$2
}
1
'  Input_file

Explanation: Adding explanation of above code here.

awk -v arr="${array[*]}" '       ##Creating a variable named arr whose value is all elements of array(shell array).
BEGIN{                           ##Starting BEGIN section of awk code here.
  FS=OFS="{"                     ##Setting FS and OFS as { here.
  num=split(arr,array," ")       ##Splitting arr variable into array named array with delimiter space and its length is stored in num variable.
  for(i=1;i<=num;i++){           ##Starting for loop from i=1 to till value of variable num.
    sub(/\.txt/,"",array[i])     ##Using sub to substitute .txt with NULL in array value whose index is variable named i.
    array1[array[i]"}"]          ##Creating an array1 whose index is array[i] value with } in it.
  }                              ##Closing for loop here.
}                                ##Closing BEGIN section of code here.
$2 in array1{                    ##Checking condition if $2 of current line is present in array named array1 then do following.
  $2="a-"$2                      ##Adding string a- with value of $2.
}                                ##Closing BLOCK for condition here.
1                                ##Mentioning 1 will print edited/non-edited line of Input_file.
'  Input_file                    ##Mentioning Input_file name here.
RavinderSingh13
  • 130,504
  • 14
  • 57
  • 93
  • Thank you very much. And how to create `string="file1, file2, file3"` when I have in my code: `names = (file1.txt, file2.txt, file3.txt)` ? – Archie Nov 01 '19 at 06:06
  • Didn't you read the comment I posted in the question? The array declaration as `names = (file1.txt, file2.txt, file3.txt)` is simply wrong! – Inian Nov 01 '19 at 06:08
  • @Archie, I have changed my code now, where array's values are having `.txt` in it and how it will handle them in awk code too, lemme know if this helps you now? – RavinderSingh13 Nov 01 '19 at 06:11
  • 1
    Thank you very much, it works. Could you please explain to me the last part please? – Archie Nov 01 '19 at 09:44
  • @Archie, your welcome, I have added detailed explanation now in my post. – RavinderSingh13 Nov 01 '19 at 09:52
  • What is the defference between `names=(file1.txt, file2.txt, file3.txt)` and `string="file1.txt, file2.txt, file3.txt"` please? I used first in my script and it does not work with the second. Thank you very much for explanation – Archie Nov 01 '19 at 09:52
  • @Archie, This is one of the way to put variables into arrays, you can change that part IMHO. – RavinderSingh13 Nov 01 '19 at 09:54
  • Why does this not work in this code please? `string=(file1.txt file2.txt file3.txt)` – Archie Nov 01 '19 at 11:49
  • @Archie, Hi as mentioned above everything is having their own method/syntax and as mentioned above only is 1 of the way of defining shell's array is what I could say. For more details and having more opinions on it IMHO you could open a new thread in bash tag too,cheers. – RavinderSingh13 Nov 01 '19 at 11:51
  • @Archie, Ok try my EDIT now, I have added way like what you need for array creation in shell. Lemme know if any queries you have? – RavinderSingh13 Nov 01 '19 at 12:03
  • 1
    Thank you very much – Archie Nov 01 '19 at 19:13
2

With sed+printf:

$ names=(file1 file2 file3) # Declare array

$ printf 's/%s/a-&/g\n' "${names[@]}" # Generate sed replacement script
s/file1/a-&/g
s/file2/a-&/g
s/file3/a-&/g

$ sed -f <(printf 's/%s/a-&/g\n' "${names[@]}") f.txt
TEXT
\connect{a-file1}
\begin{a-file2}
\connect{a-file3}
TEXT
75

If your array contains .txt suffix, use this:

$ names=(file1.txt file2.txt file3.txt) # Declare array

$ printf 's/%s/a-&/g\n' "${names[@]%.txt}" # Generate sed replacement script
s/file1/a-&/g
s/file2/a-&/g
s/file3/a-&/g

$ sed -f <(printf 's/%s/a-&/g\n' "${names[@]%.txt}") f.txt
TEXT
\connect{a-file1}
\begin{a-file2}
\connect{a-file3}
TEXT
75

If the files list contains the names which have overlapping string, you can use the word boundaries (\<,\>) to handle this. e.g.

$ cat f.txt
TEXT
\connect{file1}
\begin{file2}
\connect{file3file2}
TEXT 
75

$ names=(file1.txt file2.txt file3file2.txt) # Declare array
$ printf 's/\<%s\>/a-&/g\n' "${names[@]%.txt}" # Generate sed replacement script
s/\<file1\>/a-&/g
s/\<file2\>/a-&/g
s/\<file3file2\>/a-&/g

$ sed -f <(printf 's/\<%s\>/a-&/g\n' "${names[@]%.txt}") f.txt
TEXT
\connect{a-file1}
\begin{a-file2}
\connect{a-file3file2}
TEXT 
75
anishsane
  • 20,270
  • 5
  • 40
  • 73
  • How to make conditions that solve the following problem please? `names=(file1.txt file2.txt file3file2.txt)` I mean that there is a world in the names of files that is repeated as a part of another name of file. Then there is added `a-` more times. – Archie Nov 01 '19 at 19:15
  • You could use word boundaries (`\<`, `\>`). `printf 's/\<%s\>/a-&/g\n' "${names[@]%.txt}" ` – anishsane Nov 04 '19 at 05:03