How to avoid the command execution when appending lines to a file - linux

I'm trying to save the content of script into a file using command line, but I noticed that when the tee command detects linux commands such as $(/usr/bin/id -u), it execute the commands rather than saving the lines as it is. How to avoid the execution of the commands and saving the text exactly as I entered it?
$tee -a test.sh << EOF
if [[ $(/usr/bin/id -u) -ne 0 ]]; then
echo You are not running as the root user.
exit 1;
fi;
EOF
if [[ 502 -ne 0 ]]; then
echo You are not running as the root user.
exit 1;
fi;
Complete script contains many more lines, but I chose /usr/bin/id -u as a sample.

This has nothing to do with tee or appending to the file, it's how here-documents work. Normally variable expansion and command substitution is done in them.
Put single quotes around the EOF marker. This will treat the here-document like a single-quoted string, so that $ will not expand variables or execute command substitutions.
tee -a test.sh << 'EOF'
if [[ $(/usr/bin/id -u) -ne 0 ]]; then
echo You are not running as the root user.
exit 1;
fi;
EOF
if [[ $(/usr/bin/id -u) -ne 0 ]]; then
echo You are not running as the root user.
exit 1;
fi;

Related

Is there a way to pipe user input within a bash script into the cat command and have it save at a destination of my choosing as a text file

Something similar to this maybe:
#! /bin/bash
echo What is your name?
read name | cat > ~/Documents/file.txt
if [[ $name==Bob ]]
echo something
fi
The command creates an empty file on manjaro mint.
Your problem is that read doesn't create any output.
And you have a syntax error further down the line, it would be a good idea to put your script(s) through shellcheck.
#! /bin/bash
echo What is your name?
read -r name
echo "$name" > ~/Documents/file.txt
if [[ "$name" == "Bob" ]]; then
echo something
fi

Unable to array values outside of function in shell script [duplicate]

Please explain to me why the very last echo statement is blank? I expect that XCODE is incremented in the while loop to a value of 1:
#!/bin/bash
OUTPUT="name1 ip ip status" # normally output of another command with multi line output
if [ -z "$OUTPUT" ]
then
echo "Status WARN: No messages from SMcli"
exit $STATE_WARNING
else
echo "$OUTPUT"|while read NAME IP1 IP2 STATUS
do
if [ "$STATUS" != "Optimal" ]
then
echo "CRIT: $NAME - $STATUS"
echo $((++XCODE))
else
echo "OK: $NAME - $STATUS"
fi
done
fi
echo $XCODE
I've tried using the following statement instead of the ++XCODE method
XCODE=`expr $XCODE + 1`
and it too won't print outside of the while statement. I think I'm missing something about variable scope here, but the ol' man page isn't showing it to me.
Because you're piping into the while loop, a sub-shell is created to run the while loop.
Now this child process has its own copy of the environment and can't pass any
variables back to its parent (as in any unix process).
Therefore you'll need to restructure so that you're not piping into the loop.
Alternatively you could run in a function, for example, and echo the value you
want returned from the sub-process.
http://tldp.org/LDP/abs/html/subshells.html#SUBSHELL
The problem is that processes put together with a pipe are executed in subshells (and therefore have their own environment). Whatever happens within the while does not affect anything outside of the pipe.
Your specific example can be solved by rewriting the pipe to
while ... do ... done <<< "$OUTPUT"
or perhaps
while ... do ... done < <(echo "$OUTPUT")
This should work as well (because echo and while are in same subshell):
#!/bin/bash
cat /tmp/randomFile | (while read line
do
LINE="$LINE $line"
done && echo $LINE )
One more option:
#!/bin/bash
cat /some/file | while read line
do
var="abc"
echo $var | xsel -i -p # redirect stdin to the X primary selection
done
var=$(xsel -o -p) # redirect back to stdout
echo $var
EDIT:
Here, xsel is a requirement (install it).
Alternatively, you can use xclip:
xclip -i -selection clipboard
instead of
xsel -i -p
I got around this when I was making my own little du:
ls -l | sed '/total/d ; s/ */\t/g' | cut -f 5 |
( SUM=0; while read SIZE; do SUM=$(($SUM+$SIZE)); done; echo "$(($SUM/1024/1024/1024))GB" )
The point is that I make a subshell with ( ) containing my SUM variable and the while, but I pipe into the whole ( ) instead of into the while itself, which avoids the gotcha.
#!/bin/bash
OUTPUT="name1 ip ip status"
+export XCODE=0;
if [ -z "$OUTPUT" ]
----
echo "CRIT: $NAME - $STATUS"
- echo $((++XCODE))
+ export XCODE=$(( $XCODE + 1 ))
else
echo $XCODE
see if those changes help
Another option is to output the results into a file from the subshell and then read it in the parent shell. something like
#!/bin/bash
EXPORTFILE=/tmp/exportfile${RANDOM}
cat /tmp/randomFile | while read line
do
LINE="$LINE $line"
echo $LINE > $EXPORTFILE
done
LINE=$(cat $EXPORTFILE)

exit from STDIN from bash script when the user want to close it

I'm automating the file creation from a bash script. I generated a file rc_notes.txt which has commit messages from two tags and want to re-write that in a new file as rc_device.txt.
I want the user to write the customer release notes and exit from the BASH STDIN that I prompt in the terminal.
The problem in my script is I'm not able to trap the close of file.
Wondering how to do. I don't want to trap the close signal. I want to enter magic string example: Done or some string that triggers the closure of STDIN, that exit from the script gracefully.
My script:
#/bin/bash
set -e
echo "Creating the release candiate text"
rc_file=rc_updater_notes.txt
echo "=========Reading the released commit message file=========="
cat $rc_file
echo "=========End of the commit message file=========="
echo "Now write the release notes"
#exec < /dev/tty
while read line
do
echo "$line"
done < "${1:-/dev/stdin}" > rc_file.txt
It does create the file but I need to exit manually by entering ctrl+D or ctrl+z. I don't want to do that. Any suggestions?
To break the loop when "Done" is entered
while read line
do
if [[ $line = Done ]]; then
break;
fi
echo "$line"
done < "${1:-/dev/stdin}" > rc_file.txt
or
while read line && [[ $line != Done ]]
do
echo "$line"
done < "${1:-/dev/stdin}" > rc_file.txt

How to put if statement inside a lftp block

I am writing a bash script to download files from ftp server using lftp. I wanted to delete the files based on the second input argument.
#!/bin/bash
cd $1
lftp -u found,e48RgK7s sftp://ftp.xxx.org << EOF
set xfer:clobber on
mget *.xml
if [ $2 = "prod"]; then
echo "Production mode. Deleting files"
mrm *.xml
else
echo "Non-prod mode. Keeping files"
fi
EOF
However, if statement is not allowed in the lftp block before EOF.
Unknown command `if'.
Unknown command `then'.
Usage: rm [-r] [-f] files...
Unknown command `else'.
How do I embed if statement in such block?
A command substitution will do:
#!/bin/bash
cd "$1" || exit
mode=$2
lftp -u found,e48RgK7s sftp://ftp.xxx.org << EOF
set xfer:clobber on
mget *.xml
$(
if [ "$mode" = "prod" ]; then
echo "Production mode. Deleting." >&2 # this is logging (because of >&2)
echo "mrm *.xml" # this is substituted into the heredoc
else
echo "Non-prod mode. Keeping files" >&2
fi
)
EOF
Note that inside the substitution for the heredoc, we're routing log messages to stderr, not stdout. This is essential, because everything on stdout becomes a command substituted into the heredoc sent to lftp.
Other caveats to command substitution also apply: They run in subshells, so a assignment made inside the command substitution will not apply outside of it, and there's a performance cost to starting them.
A more efficient approach is to store your conditional components in a variable, and expand it inside the heredoc:
case $mode in
prod)
echo "Production mode. Deleting files" >&2
post_get_command='mget *.xml'
;;
*)
echo "Non-production mode. Keeping files" >&2
post_get_command=
;;
esac
lftp ... <<EOF
set xfer:clobber on
mget *.xml
$post_get_command
EOF

Terminating half of a pipe on Linux does not terminate the other half

I have a filewatch program:
#!/bin/sh
# On Linux, uses inotifywait -mre close_write, and on OS X uses fswatch.
set -e
[[ "$#" -ne 1 ]] && echo "args count" && exit 2
if [[ `uname` = "Linux" ]]; then
inotifywait -mcre close_write "$1" | sed 's/,".*",//'
elif [[ `uname` = "Darwin" ]]; then
# sed on OSX/BSD wants -l for line-buffering
fswatch "$1" | sed -l 's/^[a-f0-9]\{1,\} //'
fi
echo "fswatch: $$ exiting"
And a construct i'm trying to use from a script (and I am testing with it on the command line on CentOS now):
filewatch . | while read line; do echo "file $line has changed\!\!"; done &
So what I am hoping this does is it will let me process, one line at a time, the output of inotify, which of course sends out one line for each file it has detected a change on.
Now for my script to clean stuff up properly I need to be able to kill this whole backgrounded pipeline when the script exits.
So i run it and then if I run kill on either the first part of the pipe or the second part, the other part does not terminate.
So I think if I kill the while read line part (which should be sh (zsh in the case of running on the cmd line)) then filewatch should be receiving a SIGPIPE. Okay so I am not handling that, I guess it can keep running.
If I kill filewatch, though, it looks like zsh continues with its while read line. Why?

Resources