How to use export in bash shell? - bash

I am making one script to delete all printer jobs older than one day and no of jobs older than one day has to be print also the jobid which has been canceled
Below are few conditions which I have to follow
I do not have access to cups directory , I can not create any temporary file .
I have to use Bash shell.
I tried to use a variable, declared outside while loop but the variable value remains unchanged ~came to know it is because of child process
Tried to use export variable but that also does not work in Bash shell , same is working in ksh shell.
I have tried below logic:
count=0
currDate=`date +%Y%m%d`
lpstat -o|while read line
do
jobid=`echo $line|awk '{print $1}'|cut -d"-" -f2`
jobDate=`echo $line|awk -F ' ' 'BEGIN{OFS="-";} {print $5,$6,$7;}'`
formattedDate=`date -d"${jobDate}" +%Y%m%d`
if [ `expr $currDate - $formattedDate` -gt 1 ]
then
count=`expr $count + 1`
echo " cancelling printer job with jobid $jobid "
cancel $jobid
fi;
done
[ $count -gt 0 ] ; echo "NO of Printer jobs pending more than 1 days are $count";
Not getting how to handle the count variable as the above mentioned way it will not work,
Export is not working in bash shell , creating tmp file is not allowed.
Any suggestion to get the solution.

The problem with count is that you process it inside a new bash child process (bash creates it automatically when you pipe input into while). This is why you get back the initial value.
The solution for you would be to output the value in that child process. This should work (braces are forcing bash to create a child process that includes the output part):
count=0
currDate=`date +%Y%m%d`
lpstat -o| (while read line
do
jobid=`echo $line|awk '{print $1}'|cut -d"-" -f2`
jobDate=`echo $line|awk -F ' ' 'BEGIN{OFS="-";} {print $5,$6,$7;}'`
formattedDate=`date -d"${jobDate}" +%Y%m%d`
if [ `expr $currDate - $formattedDate` -gt 1 ]
then
count=`expr $count + 1`
echo " cancelling printer job with jobid $jobid "
cancel $jobid
fi;
done
[ $count -gt 0 ] && echo "NO of Printer jobs pending more than 1 days are $count")

The simplest solution is to reference the variable in the same shell you are setting it. Syntactically, the easiest thing to do is to add a block:
count=0
currDate=`date +%Y%m%d`
lpstat -o| { while read line # Add opening brace here
do
jobid=`echo $line|awk '{print $1}'|cut -d"-" -f2`
jobDate=`echo $line|awk -F ' ' 'BEGIN{OFS="-";} {print $5,$6,$7;}'`
formattedDate=`date -d"${jobDate}" +%Y%m%d`
if [ `expr $currDate - $formattedDate` -gt 1 ]
then
count=`expr $count + 1`
echo " cancelling printer job with jobid $jobid "
cancel $jobid
fi;
done
echo "Number of Printer jobs pending more than 1 day: $count"
} # Add closing brace here
This is not a code review site, but I feel compelled to address other elements of this code. echo $line | awk '{print $1}' | cut -d"-" -f2 is an atrocity. Rather than reading in an entire line and then parsing it with echo/awk/cut, let read do the parsing for you by assigning IFS appropriately. Stop using backticks. $() notation is better. The last line of this code [ $count -gt 0 ]; echo ... completely ignores the evaluation of $count. It would be better written [ $count -gt 0 ] || echo ... >&2 or test "$count" = 0 && echo ... >&2 (redirect the error message to file descriptor 2, since error belong on stderr).

Related

Get number of line of word number X in file

Need to make a shell script that splits every csv file that uses \n as separator, the limit per file is the number of words and
I can't cut the line in half.
Finished script with the help of a wizard!
Example:
sh SliceByWords.sh 1000 .
Slices every file by 1000 words and put every part into subfolder
function has_number_number_of_words {
re='^[0-9]+$'
if ! [[ $1 =~ $re ]] ; then
echo "error: Not a number, please run the command with the number of words per file" >&2; exit 1
fi
}
#MAIN
has_number_number_of_words $1
declare -i WORDLIMIT=$1 # N of lines to part each file
subdir="Result"
mkdir $subdir
format=*.csv
for name in $format; do mv "$name" "${name// /___}"; done
for i in $format;
do
if [[ "$i" == "$format" ]]
then
echo "No Files"
else
( locali=$(echo $i | awk '{gsub(/ /,"\\ ");print}');
localword=$i;
FILENAMEWITHOUTEXTENSION="${localword%.*}" ;
subnoext=$subdir"/"$FILENAMEWITHOUTEXTENSION;
echo Processing file "$FILENAMEWITHOUTEXTENSION";
awk -v NOEXT=$subnoext -v wl=$WORDLIMIT -F" " 'BEGIN{fn=1}{c+=NF}{sv=NOEXT"_snd_"fn".csv";print $0>sv;}c>wl{c=0;++fn;close(sv);}' $localword;
)&
fi
done
wait #wait
for name in $format; do mv "$name" "${name//___/ }"; done
echo All files done.
Since i couldnt figure out how to enter awk files with spaces , im using
for name in $format; do mv "$name" "${name//___/ }"; done
I think this would be a lot easier to handle with awk:
awk -F" " 'BEGIN{filenumber=1}{counter+=NF}{print $0 > FILENAME"_part_"filenumber} counter>1000{counter=0;++filenumber}' yourinputfile
awk here is:
Splitting each line by space -F" "
Before processing the file set the filenumber variable to 1
Bump the counter variable by the number of fields in the line {counter+=NF}
Print out the line to the file, numbered by a variable. Using the FILENAME built-in variable here to pull through yourinputfile. {print $0 > FILENAME"_part_"filenumber}
If the counter has popped over 1000, then send it back to 0 and bump the filenumber variable by 1 counter>1000{counter=0;++filenumber}
Minimized a bit:
awk -F" " 'BEGIN{fn=1}{c+=NF}{print $0>FILENAME"_part_"fn}c>1000{c=0;++fn}' yourinputfile

shell script works but drop error "line[8] expected argument ["

I have shell script that works (does what i want to do,finds if listed user is online), but each time drop error "line[8] expected argument [". I've tried using == but same thing. There's my code:
#!/bin/sh
truth=0;
until [ $truth -eq 1 ]
do
for i; do
isthere=$(who is here | awk '{print $1}' | grep $i)
if [ $isthere = $i ] #(8 line is here)
then
echo "found user: "$isthere". program now will close.";
exit 0;
fi
done
echo "user not found, retrying after 3sec...";
sleep 3;
done
Thank you for you help and time.
Looks like $isthere or $i is empty. You should quote them: if [ "$isthere" = "$i" ]
In other news: most semicolons are useless; a semicolon it is not a statement terminator, but a statement separator, along with newline.

Validating multiple inputs procured through read command

I am trying to validate multiple inputs separated by spaces(two disk names in the below case) with a shell script. But, I am un-successful in doing so. Can someone help me?
read DISK
if [ "${1}" = "" ] || [ "${2}" = "" ]
then
printf "The Disk pairs cannot be left blank. Exiting script!!!"
exit 1
else
TMP=$DISK
printf "The disks entered are $TMP"
fi
For ksh93, you can use
read -A disks
if [[ ${#disks[#]} -ne 2 ]]; then
print -u2 "You need to enter 2 disks"
exit 1
else
print "You entered: ${disks[*]}"
fi
For ksh88, use the positional parameters
read disks
set -- $disks
if [[ $# -ne 2 ]]; then
print -u2 "You need to enter 2 disks"
exit 1
else
print "You entered: $disks"
fi
The variables ${1} and ${2} are the commandline parameters and are unrelated to the last read command. There are different ways to use the DISK variable.
Once you have the DISK variable read, I would have chosen for a solution like
echo "${DISK}" | while read disk1 disk2 otherfields; do
echo "disk1=${disk1}, disk2=${disk2}"
done
# or better
disk1="${DISK% *}"; echo "${disk1}"
disk2="${DISK#* }"; echo "${disk2}"
# or worse
disk1=$(echo "${DISK}" | cut -d" " -f1)
disk2=$(echo "${DISK}" | cut -d" " -f2)
When you already know you want to split the fields, you can change your first read command. Replace read DISK with
read disk1 disk2 remaining_input

Unexpected end of file bash script

This is just a simple problem but I don't understand why I got an error here. This is just a for loop inside an if statement.
This is my code:
#!/bin/bash
if (!( -f $argv[1])) then
echo "Argv must be text file";
else if ($#argv != 1) then
echo "Max argument is 1";
else if (-f $argv[1]) then
for i in `cut -d ',' -f2 $argv[1]`
do
ping -c 3 $i;
echo "finish pinging host $i"
done
fi
Error is in line 16, which is the line after fi, that is a blank line .....
Can someone please explain why i have this error ????
many, many errors.
If I try to stay close to your example code:
#!/bin/sh
if [ ! -f "${1}" ]
then
echo "Argv must be text file";
else if [ "${#}" -ne 1 ]
then
echo "Max argument is 1";
else if [ -f "${1}" ]
then
for i in $(cat "${1}" | cut -d',' -f2 )
do
ping -c 3 "${i}";
echo "finish pinging host ${i}"
done
fi
fi
fi
another way, exiting each time the condition is not met :
#!/bin/sh
[ "${#}" -ne 1 ] && { echo "There should be 1 (and only 1) argument" ; exit 1 ; }
[ ! -f "${1}" ] && { echo "Argv must be a file." ; exit 1 ; }
[ -f "${1}" ] && {
for i in $(cat "${1}" | cut -d',' -f2 )
do
ping -c 3 "${i}";
echo "finish pinging host ${i}"
done
}
#!/usr/local/bin/bash -x
if [ ! -f "${1}" ]
then
echo "Argument must be a text file."
else
while-loop-script "${1}"
fi
I have broken this up, because I personally consider it extremely bad form to nest one function inside another; or truthfully to even have more than one function in the same file. I don't care about file size, either; I've got several scripts which are 300-500 bytes long. I'm learning FORTH; fractalism in that sense is a virtue.
# while-loop-script
while read line
do
IFS="#"
ping -c 3 "${line}"
IFS=" "
done < "${1}"
Don't use cat in order to feed individual file lines to a script; it will always fail, and bash will try and execute the output as a literal command. I thought that sed printing would work, and it often does, but for some reason it very often substitutes spaces for newlines, which is extremely annoying as well.
The only absolutely bulletproof method of feeding a line to a script that I know of, which will preserve all space and formatting, is to use while-read loops, rather than substituted for cat or for sed loops, as mentioned.
Something else which you will need to do, in order to be sure about preserving whitespace, is to set the internal field seperator (IFS) to something that you know your file will not contain, and then resetting it back to whitespace at the end of the loop.
For every opening if, you must have a corresponding closing fi. This is also true for else if. Better use elif instead
if test ! -f "$1"; then
echo "Argv must be text file";
elif test $# != 1; then
echo "Max argument is 1";
elif test -f "$1"; then
for i in `cut -d ',' -f2 "$1"`
do
ping -c 3 $i;
echo "finish pinging host $i"
done
fi
There's also no argv variable. If you want to access the command line arguments, you must use $1, $2, ...
Next point is $#argv, this evaluates to $# (number of command line args) and argv. This looks a lot like perl.
Furthermore, testing is done with either test ... or [ ... ], not ( ... )
And finally, you should enclose at least your command line arguments in double quotes "$1". If you don't and there is no command line argument, you have for example
test ! -f
instead of
test ! -f ""
This lets the test fail and go on to the second if, instead of echoing the proper message.

bash script using multiple while loops and read line

I am trying to write a bash script to create some playlists of music. The part that has me stuck is the while loop for read line. I figure I am over thinking this so I turned to stackoverflow for assistance.
# The first while loop is how many playlists I want to create
i=1
while [ $i -le $plist ]
do
echo -e "iteration $i"
i=$[$i + 1]
z=0
# This while loop is for the length of time I want the playlist to be
while [ $z -le $TOTAL ]
do
echo -e "Count $z"
z=$[$z + xxx]
# This while loop is for reading the track list previously generated.
# It would read the line, calculate the track length,
# add to $z, cp the track to a folder
while read line
do
secs=$(metaflac --show-total-samples --show-sample-rate "$line" | tr '\n' ' '
| awk '{print $1/$2}' -)
z=$[$z + $secs]
cp $line to destination folder
done
done
done

Resources