How can I use bash to replace a string in a file from another file For example:
There is a path: \\Hostname\example\example.txtI want to replace the Hostname with 162 hostnames from another file that contains the 162 hostnames then I save the result.
So the total path should be : 162 paths with different hostnames (162)
The first folder is : Paths.txt ( has 2 paths):
\\Hostname\example1\example1.txt
\\Hostname\example2\example2.txtThe Second file is: Hostname.txt ( has 162 hostnames )
I tried :
sed -i 's/Hostname/ Paths.txtBut the result is :
\\Hostname.txt\example1\example1.txt
\\Hostname.txt\example2\example2.txtIt did not read the real hostname inside the file Hostname.txt
32 Answers
This is not a straight answer to the question, but to solve this task you can use the following loop:
while IFS= read -r hostname; do \ printf '\\\\%s\\example1\\example1.txt\n' "$hostname" >> ./hosts-new.txt; \ printf '\\\\%s\\example2\\example2.txt\n' "$hostname" >> ./hosts-new.txt; \
done < ./hosts-list.txtCopy the above lines and run them as a single command (inline script).
Here are some explanations:
hosts-new.txtis the output file.hosts-list.txtis the input file containing hostnames, each at new line.IFS= read -r hostname(at each loop's iteration) will read a line of the input file and assign it as a value of the variable that we named$hostname... as @StéphaneChazelas says in his encyclopedic answer this is the canonical way to read one line of input with thereadbuiltin.The key is that
readreads words from a (possibly backslash-continued) line, where words are$IFSdelimited and backslash can be used to escape the delimiters (or continue lines). So thereadcommand should be tweaked to read lines.IFS=changes the internal field separator to the null string, thus we preserve leading and trailing whitespace in the result.The option
-r- raw input - disables interpretation of backslash escapes and line-continuation in the read data (reference).
printf '\\\\%s\\example\\example.txt\n' "$hostname"will generate the desired output for each line. By\\we are escaping the special meaning of the single\. The character\nat the end means new line.%sis a string formatted placeholder of the value of the first positional parameter (ofprintf) which is our variable$hostname.The output redirection
>>will append each line to the output file.By the part
< ./hosts-list.txtat the end we are passing the content of the input file as input of thewhileloop, which is defined by the three keywordswhile (condition); do (something); done. Sowhilereads the file line by line and, in this case, the condition is while there is an input.
Here is another version of the above, that probably will be faster because it will write to the new file only once after the loop is finished.
while IFS= read -r hostname; do \ printf '\\\\%s\\example1\\example1.txt\n' "$hostname"; \ printf '\\\\%s\\example2\\example2.txt\n' "$hostname"; \
done < ./hosts-list.txt > ./hosts-new.txtNote here is used the redirection > (instead of >>) that will override the content of the output file.
If you really want to read both the input paths and replacements from files, you could do something like this in GNU awk:
gawk ' BEGIN { PROCINFO["sorted_in"] = "@ind_num_asc" } NR==FNR { paths[FNR] = $0 next } { for(i in paths) print gensub(/\\\\Hostname/,"\\\\"$0,"1",paths[i]) }
' Paths.txt Hostname.txtYou can omit the BEGIN block if you don't care about the order of the paths for each host. You can probably safely omit the \\\\ and \\\\ in the gensub pattern and replacement (they're just included to anchor the Hostname string, in case it may may occur without the \\ prefix elsewhere in the file).
You could also consider slurping the whole content of the Paths.txt file into a single multi-line string, and applying gensub with the g global flag instead of the loop:
gawk ' NR==FNR { paths = paths (paths ? RS : "") $0 next } { print gensub(/\\\\Hostname/,"\\\\"$0,"g",paths) }
' Paths.txt Hostname.txtor slurping the paths file as a single null-delimited record in a BEGIN block
gawk -v pathsfile="Paths.txt" ' BEGIN { RS = "\0" getline paths < pathsfile RS = "\n" } { printf "%s", gensub(/\\\\Hostname/,"\\\\"$0,"g",paths) }
' Hostname.txtor (should work in any awk)
awk ' NR==FNR { paths = paths (paths ? RS : "") $0 next } { newpaths = paths gsub(/\\\\Hostname/,"\\\\"$0,newpaths) print newpaths }
' Paths.txt Hostname.txt 1