Unable to refer to a parameter in Bash - bash

I want to put TextA to the beginning of TextB by
cat TextA A TextB
The problem is that I do not know how to refer to the first and second parameters, that is TextA and TextB in the following script called A:
#!/bin/bash
cat TextA > m1
cat TextB > m2
cat m1 m2 > TextB
where m1 and m2 are temporary files.
How can you refer to the two files in the shell script?

You can use $0, $1, $2 etc. to refer to the variables in the script.
$0 is the name of the script itself
$1 is the first parameter
$2 is the second parameter
and so on
For instance, if you have this command:
a A1 A2
Then inside a you'll have:
$0 = a
$1 = A1
$2 = A2

In a bash script is the first parameter is $1, the second is $2 and so on.
If you want a default value for example the third parameter you can use:
var=${3:-"default"}

you could just use append (>>)
cat TextB >> TextA
result is that TextA precedes text TextB in TextA

I would do the following:
#!/bin/bash
if [ $# -ne 2 ]
then
echo "Prepend file with copyright notice"
echo "Usage: `basename $0` <copyright-file> <mainfile>"
exit 1
fi
copyright=$1
mainfile=$2
cat $mainfile > /tmp/m.$$
cat $copyright /tmp/m.$$ > $mainfile
#cleanup temporary files
rm /tmp/m.$$ /tmp/m2.$$

I am surprised that nobody suggests the following result
cat TextA TextB | tee > TextB
This way, you can avoid the hassle of creating a temporary file.

Looks like you can just do the following:
TextA="text a"
TextB="text b"
echo "$TextA $TextB" > file1
Or use the append (>>) operator.

Related

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

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.
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
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.
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.
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).
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

Read a file and replace ${1}, ${2}... value with string

I have a file template.txt and its content is below:
param1=${1}
param2=${2}
param3=${3}
I want to replace ${1},{2},${3}...${n} string values by elements of scriptParams variable.
The below code, only replaces first line.
scrpitParams="test1,test2,test3"
cat template.txt | for param in ${scriptParams} ; do i=$((++i)) ; sed -e "s/\${$i}/$param/" ; done
RESULT:
param1=test1
param2=${2}
param3=${3}
EXPECTED:
param1=test1
param2=test2
param3=test3
Note: I don't want to save replaced file, want to use its replaced value.
If you intend to use an array, use a real array. sed is not needed either:
$ cat template
param1=${1}
param2=${2}
param3=${3}
$ scriptParams=("test one" "test two" "test three")
$ while read -r l; do for((i=1;i<=${#scriptParams[#]};i++)); do l=${l//\$\{$i\}/${scriptParams[i-1]}}; done; echo "$l"; done < template
param1=test one
param2=test two
param3=test three
Learn to debug:
cat template.txt | for param in ${scriptParams} ; do i=$((++i)) ; echo $i - $param; done
1 - test1,test2,test3
Oops..
scriptParams="test1 test2 test3"
cat template.txt | for param in ${scriptParams} ; do i=$((++i)) ; echo $i - $param; done
1 - test1
2 - test2
3 - test3
Ok, looks better...
cat template.txt | for param in ${scriptParams} ; do i=$((++i)) ; sed -e "s/\${$i}/$param/" ; done
param1=test1
param2=${2}
param3=${3}
Ooops... so what's the problem? Well, the first sed command "eats" all the input. You haven't built a pipeline, where one sed command feeding the next... You have three seds trying to read the same input. Obviously the first one processed the whole input.
Ok, let's take a different approach, let's create the arguments for a single sed command (note: the "" is there to force echo not to interpret -e as an command line switch).
sedargs=$(for param in ${scriptParams} ; do i=$((++i)); echo "" -e "s/\${$i}/$param/"; done)
cat template.txt | sed $sedargs
param1=test1
param2=test2
param3=test3
That's it. Note that this isn't perfect, you can have all sort of problems if the replace texts are complex (e.g.: contain space).
Let me think how to do this in a better way... (well, the obvious solution which comes to mind is not to use a shell script for this task...)
Update:
If you want to build a proper pipeline, here are some solutions: How to make a pipe loop in bash
You can do that with just bash alone:
#!/bin/bash
scriptParams=("test1" "test2" "test3") ## Better store it as arrays.
while read -r line; do
for i in in "${!scriptParams[#]}"; do ## Indices of array scriptParams would be populated to i starting at 0.
line=${line/"\${$((i + 1))}"/"${scriptParams[i]}"} ## ${var/p/r} replaces patterns (p) with r in the contents of var. Here we also add 1 to the index to fit with the targets.
done
echo "<br>$line</br>"
done < template.txt
Save it in a script and run bash script.sh to get an output like this:
<br>param1=test1</br>
<br>param2=test2</br>
<br>param3=test3</br>

What output will echo > produce?

I don't have a linux at hand and instead use compileonline.com to check out some code in bash, yet I'm new to bash. Could somebody give a hand?
for var
do echo $var > fniz
cat fniz
done
arguments are 123 abc xyz
My guess is, the output would be:
123
123
abc
123
abc
xyz
But I'm not sure, whether echo $var > fniz overwrites fniz or writes a new line to it. Does it overwrite the file?
> always overwrites.
Writing a new line would be achieved by using the “append” redirection operator >>.
It overwrites the file each time
$ cat script.sh
for var in 123 abv xyz
do
echo $var > fniz
cat fniz
done
$ ./script.sh
123
abv
xyz
If you want to append, use >>

Shell Scripts Error

My assignment asks that:
Create a directory ~/UnixCourse/scriptAsst. Turn the two-line version,
above, of the substitution commands into a shell script, subst1 taking
three parameters: the string to be replaced the string with which to
replace it the name of the file in which to make the substitution.
For example,
`~/UnixCourse/scriptAsst/subst1 foo bar myFile.txt`
should replace all occurrences of foo in the file myFile.txt by bar, leaving the
original file as myFile.txt.bak.
Similarly,
`~/UnixCourse/scriptAsst/subst1 abc "" aardvark.dat`
should remove (replace by the empty string) all occurrences of abc in the file aardvark.dat with nothing, leaving the original file as aardvark.dat.bak.
My code that I came up with is:
#!/bin/bash
set p1 = "$1"
shift
set p2 = "$1"
shift
set p3 = "$*"
echo $p1
echo $p2
echo $p3
if grep "$p1" "$p3" > /dev/null; then
mv "$p3" "$p3.bak"
sed "s/$p1/$p2/g" "$p3.bak" > "$p3"
fi
When I try to run:
./subst1 foo bar myFile.txt
I keep getting:
grep: : No such file or directory
Please help!! What am I doing wrong??
This is how you set variables:
p1="$1"
shift
p2="$1"
shift
p3="$1"
or in this case simply:
p1="$1"; p2="$2"; p3="$3"
Note:
try to use meaningful variable names, or simply use $1 directly.
there is grep -q so you don't have to redirect standard output.
You don't actually need to do anything.
sed -i.bak "s/$1/$2/g" "$3"
or use the parameters directly...
#!/bin/bash
echo $1
echo $2
echo $3
if grep "$1" "$3" > /dev/null; then
mv "$3" "$3.bak"
sed "s/$1/$2/g" "$3.bak" > "$3"
fi

Opening a file in write mode

I have a file called a.txt. with values like
1
2
3
...
I want to overwrite this file but
echo "$var" >> a.txt
echo "$var1" >> a.txt
echo "$var2" >> a.txt
...
just appends. Using > is not useful as well. How can i overwrite with using >> operator in shell script?
You may want to use > for the first redirection and >> for subsequent redirections:
echo "$var" > a.txt
echo "$var1" >> a.txt
echo "$var2" >> a.txt
> truncates the file if it exists, and would do what you originally asked.
>> appends to the file if it exists.
If you want to overwrite the content of a file (not truncate it), use 1<>
e.g.:
[23:58:27 0 ~/tmp] $ echo foobar >a
[23:58:28 0 ~/tmp] $ cat a
foobar
[23:58:50 0 ~/tmp] $ echo -n bar 1<>a
[23:58:53 0 ~/tmp] $ cat a
barbar
In what way is using > not useful? That explicitly does what you want by overwriting the file, so use > for the first and then >> to append future values.
echo "$var
$var1
$var2" > a.txt
or
echo -e "$var\n$var1\n$var2" > a.txt

Resources