Home > Mobile >  How to replace a match with an entire file in BASH?
How to replace a match with an entire file in BASH?

Time:10-16

I have a line like this:

INPUT file1

How can I get bash to read that line and directly copy in the contents of "file1.txt" in place of that line? Or if it sees: INPUT file2 on a line, put in `file2.txt" etc.

The best I can do is a lot of tr commands, to paste the file together, but that seems an overly complicated solution.

'sed' also replaces lines with strings, but I don't know how to input the entire content of a file, which can be hundreds of lines into the replacement.

CodePudding user response:

Seems pretty straightforward with awk. You may want to handle errors differently/more gracefully, but:

$ cat file1
Line 1 of file 1
$ cat file2
Line 1 of file 2
$ cat input
This is some content
INPUT file1
This is more content
INPUT file2
This file does not exist
INPUT file3
$ awk '$1=="INPUT" {system("cat " $2); next}1' input
This is some content
Line 1 of file 1
This is more content
Line 1 of file 2
This file does not exist
cat: file3: No such file or directory

CodePudding user response:

A perl one-liner, using the CPAN module Path::Tiny

perl -MPath::Tiny -pe 's/INPUT (\w )/path("$1.txt")->slurp/e' input_file

use perl -i -M... to edit the file in-place.

CodePudding user response:

Not the most efficient possible way, but as an exercise I made a file to edit named x and a couple of input sources named t1 & t2.

$: cat x
a
INPUT t2
b
INPUT t1
c
$: while read k f;do sed -ni "/$k $f/!p; /$k $f/r $f" x;done< <( grep INPUT x )
$: cat x
a

here's
 ==> t2

b

this
is
file ==> t1

c

Yes, the blank lines were in the INPUT files.
This will sed your base file repeatedly, though.
The awk solution given is better, as it only reads through it once.

CodePudding user response:

If you want to do this in pure Bash, here's an example:

#!/usr/bin/env bash

if (( $# < 1 )); then
    echo "Usage: ${0##*/} FILE..."
    exit 2
fi

for file; do
    readarray -t lines < "${file}"
    for line in "${lines[@]}"; do
        if [[ "${line}" == "INPUT "* ]]; then
            cat "${line#"INPUT "}"
            continue
        fi
        echo "${line}"
    done > "${file}"
done

Save to file and run like this: ./script.sh input.txt (where input.txt is a file containing text mixed with INPUT <file> statements).

CodePudding user response:

Sed solution similar to awk given erlier:

$ cat f 
test1

INPUT f1

test2

INPUT f2

test3

$ cat f1
new string 1

$ cat f2
new string 2

$ sed 's/INPUT \(.*\)/cat \1/e' f
test1

new string 1

test2

new string 2

test3

Bash variant

while read -r line; do
    [[ $line =~ INPUT.* ]] && { tmp=($BASH_REMATCH); cat ${tmp[1]}; } || echo $line
done < f
  • Related