Not able to overwrite the file properly through unix script - shell

Below the script which I am using to overwrite the chk.sas file but unfortunately it's only overwrite the last statement of code "run;" in chk.sas file.
#!/bin/bash
x=$(pwd)
echo "libname sasdata '$x';" > $x/chk.sas
echo "proc print data=sasdata.data ;" > $x/chk.sas
echo "run;" > $x/chk.sas
sas chk.sas
exit 0
Below is the desired result which I am expecting from the script but I am not sure where I am doing mistake in the script.
libname sasdata '/home/usr' ;
proc print data=sasdata.data ;
run;
Thank you in advance for help.

Each time that > is used, the previous file is overwritten. One solution is to use >> which means append. Another solution is to group all the echo commands together and just use > once:
#!/bin/bash
x=$(pwd)
{
echo "libname sasdata '$x';"
echo "proc print data=sasdata.data ;"
echo "run;"
} > "$x/chk.sas"
sas "$x/chk.sas"
Notes
Unless you specifically want word splitting and pathname expansion, put shell variables inside double quotes. Thus, above, we use "$x/chk.sas" in place of $x/chk.sas.
Use exit 0 if you want the script to return a success exit code even if the sas command failed. If you omit the exit command, the script will exit with the exit code of the last command run (in this case, sas). This is likely more informative for the whatever code invokes the script.
Alternative: here-doc
Another way to write several lines to a file is to use a here-doc:
#!/bin/bash
x=$(pwd)
cat >"$x/chk.sas" <<EOF
libname sasdata '$x';
proc print data=sasdata.data ;
run;
EOF
sas "$x/chk.sas"
For more information, look at the section entitled Here Documents in man bash.

Related

printing output of command history 1 from shell script

Here's my problem, from console if I type the below,
var=`history 1`
echo $var
I get the desired output. But when I do the same inside a shell script, it is not showing any output. Also, for other commands like pwd, ls etc, the script shows the desired output without any issue.
As value of variable contains space, add quotes around it.
E.g.:
var='history 1'
echo $var
I believe all you need is this as follows:
1- Ask user for the number till which user need to print the history in script.
2- Run the script and take Input from user and get the output as follows:
cat get_history.ksh
echo "Enter the line number of history which you want to get.."
read number
if [[ $# -eq 0 ]]
then
echo "Usage of script: get_history.ksh number_of_lines"
exit
else
history "$number"
fi
Added logic where it will check arguments if number of arguments passed is 0 then it will exit from script then.
By default history is turned off in a script, therefore you need to turn it on:
set -o history
var=$(history 1)
echo "$var"
Note the preferred use of $( ) rather than the deprecated backticks.
However, this will only look at the history of the current process, that is this shell script, so it is fairly useless.

Read command line arguments with input redirection operator in bash

I need to read command line arguments. First arg is script name. second one is redirection operator i.e. "<" and third one is input filename. When I tried to use "$#", I got 0. When I used "$*", it gave me nothing. I have to use "<" this operator. My input file consists of all user input data. If I don't use the operator, It asks user for the input. Can someone please help me? Thank you !
Command Line :
./script_name < input_file
Script:
echo "$*" # gave nothing
echo "$#" # gave me 0
I need to read input filename and store it to some variable. Then I have to change the extension of it. Any help/suggestions should be appreciated.
When a user runs:
./script_name <input_file
...that's exactly equivalent to if they did the following:
(exec <input_file; exec ./script_name)
...first redirecting stdin from input_file, then invoking the script named ./script_name without any arguments.
There are operating-system-specific interfaces you can use to get the filename associated with a handle (when it has one), but to use one of these would make your script only able to run on an operating system providing that interface; it's not worth it.
# very, very linux-specific, won't work for "cat foo | ./yourscript", generally evil
if filename=$(readlink /proc/self/fd/0) && [[ -e $filename ]]; then
set -- "$#" "$filename" # append filename to the end of the argument list
fi
If you want to avoid prompting for input when an argument is given, and to have the filename of that argument, then don't take it on stdin but as an argument, and do the redirection yourself within the script:
#!/bin/bash
if [[ $1 ]]; then
exec <"$1" # this redirects your stdin to come from the file
fi
# ...put other logic here...
...and have users invoke your script as:
./script_name input_file
Just as ./yourscript <filename runs yourscript with the contents of filename on its standard input, a script invoked with ./yourscript filename which invokes exec <"$1" will have the contents of filename on its stdin after executing that command.
< is used for input redirection. And whatever is at the right side of < is NOT a command line argument.
So, when you do ./script_name < input_file , there will be zero (0) command line arguments passed to the script, hence $# will be zero.
For your puprpose you need to call your script as:
./script_name input_file
And in your script you can change the extension with something like:
mv -- "$1" "${1}_new_extension"
Edit: This was not what OP wanted to do.
Altough, there is already another spot on answer, I will write this for the sake of completeness. If you have to use the '<' redirection you can do something like this in your script.
while read filename; do
mv -- "$filename" "${filename}_bak"
done
And call the script as, ./script < input_file. However, note that you will not be able to take inputs from stdin in this case.
Unfortunately, if you're hoping to take redirection operators as arguments to your script, you're not going to be able to do that without surrounding your command line arguments in quotes:
./script_name "<input_file"
The reason for this is that the shell (at least bash or zsh) processes the command before ever invoking your script. When the shell interprets your command, it reads:
[shell command (./script_name)][shell input redirection (<input_file)]
invoking your script with quotes effectively results in:
[shell command (./script_name)][script argument ("<input_file")]
Sorry this is a few years late; hopefully someone will find this useful.

How to prevent bash script from putting all output into one line?

I have the following bash script top_script.sh
#!/bin/bash
# "Usage: $0 JOBNAME LOGFILE"
JOBNAME=$1
LOGFILE=$2
JOB_OUTPUT=$($1 2>&1)
echo ${JOB_OUTPUT} >> "${LOGFILE}"
that is supposed to be invoked like this
top_script.sh script_to_run.sh log.txt
If script_to_run.sh has multiple echo statements, e.g.
echo Line 1 from $0
echo Line 2 from $0
then what I get in log.txt is
Line 1 from script_to_run.sh Line 2 from script_to_run.sh
i.e. all this output gets concatenated into a single line. I suspect that the reason is line #5 in the first code block above. How can I modify it to ensure separate echos print into separate lines in log.txt?
Not that it really matters, but in case you are wondering, top_script.sh gets generated automatically form some a config file.
echo "${JOB_OUTPUT}" >> "${LOGFILE}"

Store command to file

I'd like to create a script A which creates a script B. Script B creates a directory. So I created a file with this content, grant x permission to it, then execute it. Unfortunately it doesn't run as I expect. It makes directory first then create an empty file. Why?
#!/bin/bash
batch=`mkdir /home/hieund/bpl`
echo $batch > newfile
Update:
After trying your solution, I have:
#!/bin/bash
$myPath=$HOME/bpl
batch='mkdir ' $myPath
echo $batch > newfile
It doesn't work as well. Same unexpected behavior.
Update:
#!/bin/bash
$myPath=$HOME/bpl
batch="mkdir $myPath"
echo $batch > newfile
It doesn't work too. Same unexpected behavior.
bash: /home/hieund/bpl=/home/hieund/bpl: No such file or directory
It makes a directory at the moment of assignment, because you said this with "command substitution"
batch=`mkdir /home/hieund/bpl`
The flow of execution
mkdir /home/... - creates the directory - because of backticks - command substitution
the mkdir returns nothing, therefore
the assignment is like batch= (it assigns nothing)
the echo $batch echoes the "nothing" so: echo > newfile
you should to use
batch='mkdir /home/hieund/bpl'
for embedding variable use double quotes
batch="mkdir $myPath"
You always can use the bash -x script - to show what is executing. E.g. having a script myscript.sh
#!/bin/bash
MYDIR="./somedir"
batch="mkdir $MYDIR"
echo "$batch" > newfile
the command
bash -x myscript.sh
will show the execution of command and arguments. (note, not shown redirections)
+ MYDIR=./somedir
+ batch='mkdir ./somedir'
+ echo 'mkdir ./somedir'
One comment: You should generally assign things to variables with double quotes, because you can avoid problems with spaces. Note
myvar=$VAR/some
and
myvar="$VAR/some"
makes a big difference when the $VAR contains spaces.
Because backticks are not used for strings.
batch="mkdir /home/hieund/bpl"

How to save the command you are about to execute in bash?

Is there a better way to save a command line before it it executed?
A number of my /bin/bash scripts construct a very long command line. I generally save the command line to a text file for easier debugging and (sometimes) execution.
My code is littered with this idiom:
echo >saved.txt cd $NEW_PLACE '&&' command.py --flag $FOO $LOTS $OF $OTHER $VARIABLES
cd $NEW_PLACE && command.py --flag $FOO $LOTS $OF $OTHER $VARIABLES
Obviously updating code in two places is error-prone. Less obvious is that Certain parts need to be quoted in the first line but not the next. Thus, I can not do the update by simple copy-and-paste. If the command includes quotes, it gets even more complicated.
There has got to be a better way! Suggestions?
How about creating a helper function which logs and then executes the command? "$#" will expand to whatever command you pass in.
log() {
echo "$#" >> /tmp/cmd.log
"$#"
}
Use it by simply prepending log to any existing command. It won't handle && or || though, so you'll have to log those commands separately.
log cd $NEW_PLACE && log command.py --flag $FOO $LOTS $OF $OTHER $VARIABLES
are you looking for set -x (or bash -x)? This writes every command to standard out after executing.
use script and you will get archived everything.
use -x for tracing your script, e.g. run them as bash -x script_name args....
use set -x in your current bash (you will get echoed your commands with substitued globs and variables
combine 2 and 3 with the 1
If you just execute the command file immediately after creating it, you will only need to construct the command once, with one level of escapes.
If that would create too many discrete little command files, you could create shell procedures and then run an individual one.
(echo fun123 '()' {
echo echo something important
echo }
) > saved.txt
. saved.txt
fun123
It sounds like your goal is to keep a good log of what your script did so that you can debug it when things go bad. I would suggest using the -x parameter in your shebang like so:
#!/bin/sh -x
# the -x above makes bash print out every command before it is executed.
# you can also use the -e option to make bash exit immediately if any command
# returns a non-zero return code.
Also, see my answer on a previous question about redirecting all of this debug output to a log when --log is passed into your shell script. This will redirect all stdout and stderr. Occasionally, you'll still want to write to the terminal to give the user feedback. You can do this by saving stdout to a new file descriptor and using that with echo (or other programs):
exec 3>&1 # save stdout to fd 3
# perform log redirection as per above linked answer
# now all stdout and stderr will be redirected to the file and console.
# remove the `tee` command if you want it to go just to the file.
# now if you want to write to the original stdout (i.e. terminal)
echo "Hello World" >&3
# "Hello World" will be written to the terminal and not the logs.
I suggest you look into the xargs command. It was made to solve the problem of programtically building up argument lists and passing them off to executables for batch processing
http://en.wikipedia.org/wiki/Xargs

Resources