Writing output into a file via linux script? - linux

I have to write a script in linux that saves one line of text to a file and then appends a new line. What I currently have is something like:
read "This line will be saved to text." Text1
$Text1 > $Script.txt
read "This line will be appended to text." Text2
$Text2 >> $Script.txt

One of the main benefits of scripting is that you can automate processes. Using
read like you have destroys that. You can accept input from the user without
losing automation:
#!/bin/sh
if [ "$#" != 3 ]
then
echo 'script.sh [Text1] [Text2] [Script]'
exit
fi
printf '%s\n' "$1" "$2" > "$3"

Assuming you don't mind if the second line of your output file is overwritten (not appended) every time the script is run; this might do.
#!/bin/sh
output_file=output.dat
if [ -z "$1" ] || [ -z "$2" ]; then echo Need at least two arguments.; fi
line1=$1; line2=$2
echo $line1 > $output_file
echo $line2 >> $output_file
Executing the script:
# chmod +x foo.sh
# ./foo.sh
Need at least two arguments.
# ./foo.sh hello world
# cat output.dat
hello
world

Related

Need to verify every file passed as an argument on the command line exists using a shell script

I am looking to create a shell script that reads command line arguments, then concatenates the contents of those files and print it to stdout. I need to verify the files passed to the command line exist.
I have written some code so far, but the script only works if only one command line argument is passed. If passing more than one argument, the error checking I have tried does not work.
#!/bin/bash
if [ $# -eq 0 ]; then
echo -e "Usage: concat FILE ... \nDescription: Concatenates FILE(s)
to standard output separating them with divider -----."
exit 1
fi
for var in "$#"
do
if [[ ! -e $# ]]; then
echo "One or more files does not exist"
exit 1
fi
done
for var in "$#"
do
if [ -f $var ]; then
cat $var
echo "-----"
exit 0
fi
done
I need to fix the error checking on this so that every command line argument is checked to be an existing file. If a file does not exist, the error must be printed to stderr and nothing should be printed to stdout.
You have a bug in line 11:
if [[ ! -e $# ]]; then
You do need to check for a given file here using $var like that:
if [[ ! -e "$var" ]]; then
And you exit prematurely in line 23 - you will always print only a
single file. And remember to always quote your variable because
otherwise your script would not run correctly on files that have a whitespaces in the name, for example:
$ echo a line > 'a b'
$ cat 'a b'
a line
$ ./concat.sh 'a b'
cat: a: No such file or directory
cat: b: No such file or directory
-----.
You said:
if a file does not exist, the error must be printed to stderr and
nothing should be printed to stdout.
You aren't printing anything to stderr at the moment, if you want to
you should do:
echo ... >&2
And you should use printf instead of echo as it's more portable
even though you're using Bash.
All in all, your script could look like this:
#!/bin/bash
if [ $# -eq 0 ]; then
printf "Usage: concat FILE ... \nDescription: Concatenates FILE(s) to standard output separating them with divider -----.\n" >&2
exit 1
fi
for var in "$#"
do
if [[ ! -e "$var" ]]; then
printf "One or more files does not exist\n" >&2
exit 1
fi
done
for var in "$#"
do
if [ -f "$var" ]; then
cat "$var"
printf -- "-----\n"
fi
done
exit 0

Using sed after cat << 'EOT' to substitute just one variable inside the generated script

I'm building a script for php-fpm compilation, installation and deployment in ubuntu 14. At one point, I have got to generate another file using this main script. The resulting file is a script and should have all variables BUT one NOT expanded.
So I started with cat << 'EOT' in will of resolving the thing after the file generation with sed. But I find myself in a "logic" blackhole.
As for the EOT quoting beeing an issue for expanding just one variable, the same is for the sed line. I went straight writing the following, then laught at it without even executing it, of course.
sed -i 's/\$PhpBuildVer\/$PhpBuildVer' /etc/init.d/php-$PhpBuildVer-fpm
OR
sed -i "s/\$PhpBuildVer\/$PhpBuildVer" /etc/init.d/php-$PhpBuildVer-fpm
both would fail, while I need the first pattern to be the "$PhpBuildVer" itself and the other one beeing the expanded variable, for instance, 7.1.10.
How would I perform this substituion with either sed or another GNU Linux command?
This is my script, most of the parts have been cut-off as non question related.
#!/bin/bash
PhpBuildVer="7.1.10"
... #removed non relevant parts of the script
cat << 'EOT' >> /etc/init.d/php-$PhpBuildVer-fpm
#! /bin/sh
### BEGIN INIT INFO
# Provides: php-$PhpBuildVer-fpm
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts php-$PhpBuildVer-fpm
# Description: starts the PHP FastCGI Process Manager daemon
### END INIT INFO
php_fpm_BIN=/opt/php-$PhpBuildVer/sbin/php-fpm
php_fpm_CONF=/opt/php-$PhpBuildVer/etc/php-fpm.conf
php_fpm_PID=/opt/php-$PhpBuildVer/var/run/php-fpm.pid
php_opts="--fpm-config $php_fpm_CONF"
wait_for_pid () {
try=0
while test $try -lt 35 ; do
case "$1" in
'created')
if [ -f "$2" ] ; then
try=''
break
fi
;;
'removed')
if [ ! -f "$2" ] ; then
try=''
break
fi
;;
esac
echo -n .
try=`expr $try + 1`
sleep 1
done
}
case "$1" in
start)
echo -n "Starting php-fpm "
$php_fpm_BIN $php_opts
if [ "$?" != 0 ] ; then
echo " failed"
exit 1
fi
wait_for_pid created $php_fpm_PID
if [ -n "$try" ] ; then
echo " failed"
exit 1
else
echo " done"
fi
;;
stop)
echo -n "Gracefully shutting down php-fpm "
if [ ! -r $php_fpm_PID ] ; then
echo "warning, no pid file found - php-fpm is not running ?"
exit 1
fi
kill -QUIT `cat $php_fpm_PID`
wait_for_pid removed $php_fpm_PID
if [ -n "$try" ] ; then
echo " failed. Use force-exit"
exit 1
else
echo " done"
echo " done"
fi
;;
force-quit)
echo -n "Terminating php-fpm "
if [ ! -r $php_fpm_PID ] ; then
echo "warning, no pid file found - php-fpm is not running ?"
exit 1
fi
kill -TERM `cat $php_fpm_PID`
wait_for_pid removed $php_fpm_PID
if [ -n "$try" ] ; then
echo " failed"
exit 1
else
echo " done"
fi
;;
restart)
$0 stop
$0 start
;;
reload)
echo -n "Reload service php-fpm "
if [ ! -r $php_fpm_PID ] ; then
echo "warning, no pid file found - php-fpm is not running ?"
exit 1
fi
kill -USR2 `cat $php_fpm_PID`
echo " done"
;;
*)
echo "Usage: $0 {start|stop|force-quit|restart|reload}"
exit 1
;;
esac
EOF
#Here the variable should be substituted.
chmod 755 /etc/init.d/php-$PhpBuildVer-fpm
... #removed non relevant parts of the script
I am not 100% sure, but I think what you are looking for is:
sed -i 's/\$PhpBuildVer/'"$PhpBuildVer"'/' /etc/init.d/php-$PhpBuildVer-fpm
You can actually put two quoted expressions right next to each other in bash. E.g., echo '12'"34"'56' will output 123456. In this case, the first \$PhpBuildVer is in '' so it can match literally, and the second is in "" so that it will be expanded.
(But maybe you should consider using a template file and php, or (blatant plug)
perlpp* to build the script, rather than inlining all the text into your main script. ;) )
Edit by the way, using cat ... >> rather than cat ... > means you will be appending to the script unless you have rmed it somewhere in the code you didn't show.
Edit 2 If $PhpBuildVer has any characters in it that sed interprets in the replacement text, you might need to escape it:
repl_text="$(sed -e 's/[\/&]/\\&/g' <<<"$PhpBuildVer")"
sed -i 's/\$PhpBuildVer/'"$repl_text"'/' /etc/init.d/php-$PhpBuildVer-fpm
Thanks to this answer by Pianosaurus.
Tested example
I put this in make.sh:
#!/bin/bash
f=42 # The variable we are going to substitute
cat <<'EOT' >"test-$f.sh" # The script we are generating
#!/bin/sh
# Provides: test-$f.sh
echo 'Hello, world!'
EOT
echo "test-$f.sh before substitution is:"
echo "---------"
cat "test-$f.sh"
echo "---------"
sed -i 's/\$f/'"$f"'/' "test-$f.sh" # The substitution, from above
echo "test-$f.sh after substitution is:"
echo "---------"
cat "test-$f.sh"
echo "---------"
The output I get is:
test-42.sh before substitution is:
---------
#!/bin/sh
# Provides: test-$f.sh
echo 'Hello, world!'
---------
(note the literal $f)
test-42.sh after substitution is:
---------
#!/bin/sh
# Provides: test-42.sh
echo 'Hello, world!'
---------
(now the $f is gone, and has been replaced with its value, 42)
perlpp example
Since *I am presently the maintainer of perlpp, I'll give you that example, too :) . In a template file that I called test.template, I put:
#!/bin/sh
# Provides: test-<?= $S{ver} ?>.sh
echo 'Hello, world!'
That was exactly the content of the script I wanted, but with <?= $S{ver} ?> where I wanted to do the substitution. I then ran
perlpp -s ver=\'7.1.10\' test.template
(with escaped quotes to pass them to perl) and got the output:
#!/bin/sh
# Provides: test-7.1.10.sh
echo 'Hello, world!'
:)
Any -s name=\'value\' command-line argument to perlpp creates $S{name}, which you can refer to in the template.
<?= expr ?> prints the value of expression expr
Therefore, <?= $S{name} ?> outputs the value given on the command line for name.
Just break up the heredoc. eg
cat > file << 'EOF'
This line will not be interpolated: $FOO
EOF
cat >> file << EOF
and this line will: $FOO
EOF
If for some reason you do want to used sed as well, don't do it after, just use it instead of cat:
sed 's#foo#bar#g' >> file << EOF
this line's foo is changed by sed, with interpolated $variables
EOF

Reading variables from config file in Shell

I have question about Linux shell scripts. My question is realy abstract, so may not make sense. The idea is having 1 script and 2 config files.
Script can be like (drinkOutput.sh):
#!/bin/bash
echo -e " $1 \n"
echo -e " $2 \n"
First Config file contain (beer.conf):
drink1="heineken"
drink2="argus"
Second Config file contain (vine.conf):
drink1="chardonnay"
drink2="hibernal"
The key thing is calling the script. It has to be in next format (or with parameter)
./drinkOutput.sh beer.conf
In this case I need to have in $1 heineken and in $2 argus (inside of drinkOutput script). For
./drinkOutput.sh vine.conf
I need to get back into drinkOutput.sh chardonnay and hibernal.
Does anybody know? Thanks for any tips
You can source the config files if they are in the right format (and it seems it is in your example).
drinkOutput()
{
echo "$1"
echo "$2"
}
conf="$1"
source "$conf"
drinkOutput "$drink1" "$drink2"
If is possible if your script calls itself with the proper arguments after having parsed them from the conf file:
if [ $# == 2 ] ; then
# The arguments are correctly set in the sub-shell.
# 2 arguments: do something with them
echo magic happens: $1 $2
elif [ $# == 1 ] ; then
# 1 argument: conf file: parse conf file
arg1=`sed -n -e 's#drink1="\(.*\)"#\1#p' $1`
arg2=`sed -n -e 's#drink2="\(.*\)"#\1#p' $1`
$0 $arg1 $arg2
else
# error
echo "wrong args"
fi
test:
$ drinkOutput.sh beer.conf
magic happens: heineken argus

What is use of this -f in this statement?

I am not getting the use of -f from this statement in this program.
if [ -f $file ]
I got if but why we use -f here?
Here is whole program:
case $ch in
1)
echo -e "\n This is File Create Operation"
echo -e "\n Please Enter File Name:"
read file
if [ -f $file ]
then
echo -e "\n File already existing!!!"
else
touch $file
echo -e "\n File Created!!!"
echo -e "\n Created File can be Checked From Following Table"
ls -t|head -n 15
fi
;;
[ is usually both an alias to test and a shell builtin. [ something ] is equivalent to test something
See help [ in bash to learn the bash builtin's version, and man test to learn about the non-builtin binary /bin/test.
you'll see that [ -f file ] is true if file exist and is a regular file, and false otherwise (ie it returns 0 only if file is a regular file)

Parsing Command Output in Bash Script

I want to run a command that gives the following output and parse it:
[VDB VIEW]
[VDB] vhctest
[BACKEND] domain.computername: ENABLED:RW:CONSISTENT
[BACKEND] domain.computername: ENABLED:RW:CONSISTENT
...
I'm only interested in some key works, such as 'ENABLED' etc. I can't search just for ENABLED as I need to parse each line at a time.
This is my first script, and I want to know if anyone can help me?
EDIT:
I now have:
cmdout=`mycommand`
while read -r line
do
#check for key words in $line
done < $cmdout
I thought this did what I wanted but it always seems to output the following right before the command output.
./myscript.sh: 29: cannot open ... : No such file
I don't want to write to a file to have to achieve this.
Here is the psudo code:
cmdout=`mycommand`
loop each line in $cmdout
if line contains $1
if line contains $2
output 1
else
output 0
The reason for the error is that
done < $cmdout
thinks that the contents of $cmdout is a filename.
You can either do:
done <<< $cmdout
or
done <<EOF
$cmdout
EOF
or
done < <(mycommand) # without using the variable at all
or
done <<< $(mycommand)
or
done <<EOF
$(mycommand)
EOF
or
mycommand | while
...
done
However, the last one creates a subshell and any variables set in the loop will be lost when the loop exits.
"How can I read a file (data stream, variable) line-by-line (and/or field-by-field)?"
"I set variables in a loop. Why do they suddenly disappear after the loop terminates? Or, why can't I pipe data to read?"
$ cat test.sh
#!/bin/bash
while read line ; do
if [ `echo $line|grep "$1" | wc -l` != 0 ]; then
if [ `echo $line|grep "$2" | wc -l` != 0 ]; then
echo "output 1"
else
echo "output 0"
fi
fi
done
USAGE
$ cat in.txt | ./test.sh ENABLED RW
output 1
output 1
This isn't the best solution, but its a word by word translation of what you want and should give you something to start with and add your own logic

Resources