What output will echo > produce? - bash

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

Related

How can bash read from piped input or else from the command line argument

I would like to read some data either from pipe or from the command line arguments (say $1), whichever is provided (priority has pipe).
This fragment tells me if the pipe was open or not but I don't know what to put inside in order not to block the script (test.sh) (using read or cat)
if [ -t 0 ]
then
echo nopipe
DATA=$1
else
echo pipe
# what here?
# read from pipe into $DATA
fi
echo $DATA
Executing the test.sh script above I should get the following output:
$ echo 1234 | test.sh
1234
$ test.sh 123
123
$ echo 1234 | test.sh 123
1234
You can read all of stdin into a variable with:
data=$(cat)
Note that what you're describing is non-canonical behavior. Good Unix citizens will:
Read from a filename if supplied as an argument (regardless of whether stdin is a tty)
Read from stdin if no file is supplied
This is what you see in sed, grep, cat, awk, wc and nl to name only a few.
Anyways, here's your example with the requested feature demonstrated:
$ cat script
#!/bin/bash
if [ -t 0 ]
then
echo nopipe
data=$1
else
echo pipe
data=$(cat)
fi
echo "$data"
$ ./script 1234
nopipe
1234
$ echo 1234 | ./script
pipe
1234

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>

How to append strings to the same line instead of creating a new line?

Redirection to a file is very usefull to append a string as a new line to a file, like
echo "foo" >> file.txt
echo "bar" >> file.txt
Result:
foo
bar
But is it also possible to redirect a string to the same line in the file ?
Example:
echo "foo" <redirection-command-for-same-line> file.txt
echo "bar" <redirection-command-for-same-line> file.txt
Result:
foobar
The newline is added by echo, not by the redirection. Just pass the -n switch to echo to suppress it:
echo -n "foo" >> file.txt
echo -n "bar" >> file.txt
-n do not output the trailing newline
An alternate way to echo results to one line would be to simply assign the results to variables. Example:
j=$(echo foo)
i=$(echo bar)
echo $j$i
foobar
echo $i $j
bar foo
This is particularly useful when you have more complex functions, maybe a complex 'awk' statement to pull out a particular cell in a row, then pair it with another set.

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

Unable to refer to a parameter in 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.

Resources