Bash Script ouputs Command Not Found Repeatedly - bash

I am trying to create a bash script that will write to text files the output of some pacman queries mainly what packages are installed locally, packages that are installed as dependencies, packages that are orphans, and what packages require what dependencies. Currently, I am in the middle of solving an issue that is preventing my from writing what packages require what dependencies. I am using the following bash code:
#!/bin/bash
set -e -u
#Switch to PWD
cd /home/$USER/System/scripts/pacman-queries-output/
#Get the current date
DATE=`date +%m%d%Y`
#Pacman Queries
pacman --query -e >pacman_installed$DATE.txt
pacman --query -d >pacman_dependencies$DATE.txt
pacman -Qdt >pacman_orphans$DATE.txt
while read package_desc
do
package_name=$(echo $package_desc| cut -d' ' -f 1)
check_if_none=$(pacman -Qi $package_name | grep "Req" | sed -e 's/Required By : //g')
if $check_if_none != "Required By : None"
then
echo $package_name >>pacman_required_by$DATE.txt
pacman -Qi $package_name | grep "Req" | sed -e 's/Required By : //g' >>pacman_required_by$DATE.txt
fi
done < $PWD/pacman_installed$DATE.txt
echo 'Completed 'basename
However, the while loop doesn't seem to create and/or write to the text file I specified instead it echoes this multiple times in the terminal:
./pacman-queries.sh: line 20: Required: command not found
The following is one of the iterations of the while loop that is displayed when running bash -x pacman-queries.sh:
+ read package_desc
++ echo aesfix 1.0.1-4
++ cut '-d ' -f 1
+ package_name=aesfix
++ pacman -Qi aesfix
++ grep Req
++ sed -e 's/Required By : //g'
+ check_if_none='Required By : None'
+ Required By : None '!=' 'Required By : None'
pacman-queries.sh: line 20: Required: command not found
Could anyone suggest any solution that they might have to solve this issue? Thank you in advance.

if $check_if_none != "Required By : None"
If the check_if_none has the string:
check_if_none="Required By : None"
Then it gets expanded to:
if Required By : None != "Required By : None"
if tries to execute the command passed to it. So it will try to execute the command Required. And it does not find such command.
How to fix it:
use test [ .. ] or [[ ... ]] to do comparison in bash
always quote your variables "$varvar"
if [ "$check_if_none" != "Required By : None" ]
Also:
don't use backticks. They are less readable, can't be nested, and are deprecated.
You script after some fixing may look like this:
#!/bin/bash
set -e -u
#Switch to PWD
cd "/home/$USER/System/scripts/pacman-queries-output/"
#Get the current date
DATE=$(date +%m%d%Y)
#Pacman Queries
pacman --query -e >"pacman_installed$DATE.txt"
pacman --query -d >"pacman_dependencies$DATE.txt"
pacman -Qdt >"pacman_orphans$DATE.txt"
while IFS= read -r package_desc; do
package_name=$(echo "$package_desc" | cut -d' ' -f 1)
# renamed from check_if_none
# some newlines for readability
Required=$(
pacman -Qi "$package_name" |
grep "Req" |
sed -e 's/Required By : //g'
)
if [ "$Required" != "Required By : None" ]; then
echo "$package_name"
# runnung `pacman -Q` twice is just throwing cpu cycles...
echo "$Required"
fi
# All output goes into required_by - moved it here
# also changed `>>` into `>`
done < "$PWD/pacman_installed$DATE.txt" > "pacman_required_by$DATE.txt"
echo 'Completed 'basename

Related

how to stop if statement from executing a command bash?

I am trying to check if a particular string contains a substring. It happens that the substring is also a command, which I do not want to execute but every time the if statement is hit, it does try to execute. How to stop it?
This is the part I am talking about:
if [[ "$installed" == *"$package"* ]]
then
"$package" >> $filename
fi
And this is the rest of the script:
read -p "enter first ID from yum History to search " startId
read -p "enter the last ID from yum history to search " endId
#getting undone operations so we can skip on them
undone=$(sudo yum history info $startId..$endId | grep Command | grep undo | awk {print$6}'|column)
echo "those operations were undone so we won\'t take them under consideration "$undone
#getting a list of installed packages
installed=$(sudo yum list installed | column)
read -r -p "enter an absolute path and a file name to save your result " filename
$(sudo touch "$filename")
#setting up a counter for a while loop
counter=1
while [ $counter -le $endId ]
do
if [[ "$undone" != *"$counter"* ]]
then
printer=1
package="something"
while [[ ! -z $package ]]
do
#get info about a particular history record, get the command, split by ":",
#print what's on the other side of ":", remove "re" and "install", and -y,
#separate by spaces and print 1st word and then second.
package=`sudo yum history info $counter | grep Command | awk -F":" '{print$2}'| grep install | sed s/'re'// | sed s/'install'// | sed s/'-y'//| awk -F' ' '{print$'$printer'}'`
((printer++))
HERE>>>>> if [[ "$installed" == *"$package"* ]]
then
"$package" >> $filename
fi
done
((counter++))
else
echo "this operation was undone".$counter
((counter++))
fi
done
$(sudo sort -u $filename -o $filename)
Output when running with -x option:
+ [[ Loaded plugins: langpacks, product-id, search-disabled-repos, subscription-
: manager
Installed Packages
.
.
.
zlib-devel.x86_64 1.2.7-18.el7 #rhel-server-local == *mcelog.x86_64* ]]
+ mcelog.x86_64
history: line 43: mcelog.x86_64: command not found
+ [[ ! -z mcelog.x86_64 ]]

For loop and confition including Curl command

I am trying to write a script where I enumerate users by checking the HTTP reponse length. I want to get output "good" when response is not equal 23, however I get these errors now:
for ((i=113;i<=115;i++)); do
if [[curl -i -s -k -X 'GET' "http://myurl/some.asp?q=$i" |
grep Content-Length | cut -d' ' -f2 != 23]]
then
echo "good"
fi
done
Output:
bash: [[curl: command not found
cut: !=: No such file or directory
cut: 23]]: No such file or directory
cut: !=: No such file or directory
cut: 23]]: No such file or directory
bash: [[curl: command not found
cut: !=: No such file or directory
cut: 23]]: No such file or directory
bash: [[curl: command not found
If I simply make a script without if condition, then it works well:
for ((i=113;i<=115;i++)); do
curl -i -s -k -X 'GET' "http://myurl/some.asp?q=$i" |
grep Content-Length
done
I checked many examples but can't seem to figure out where I am doing wrong.
After updating your initial error, you may have a syntax like (suggestion: put some effort on format, so that it is more clear what you have and what may be wrong):
for ((i=113;i<=115;i++))
do
if [[ curl -i -s -k -X 'GET' "http://myurl/some.asp?q=$i" | grep Content-Length | cut -d' ' -f2 != 23 ]]
then
echo "good"
fi
done
This is returning you the error:
bash: conditional binary operator expected bash: syntax error near
-i'`
Which is normal, because you are basically saying:
if [[ command ]]; then ...
Where command is a set of multiple piped commands. However, in [[ you just can add expressions on the form "$var" -eq 23 or "$(command)" -ne 23.
So use $( ) to execute the command: if [[ "$(command)" -ne 23 ]]:
if [[ "$(curl -i -s -k -X 'GET' "http://myurl/some.asp?q=$i" | grep Content-Length | cut -d' ' -f2)" -ne 23 ]]
Note I am using -ne to perform an integer comparison that means "not equal to".
Finally, notice that awk alone can do what grep and cut do in two steps:
... | grep "Content-Length" | cut -d' ' -f2
This means: check the line containing "Content-Length" and print its second field. awk makes it simple by saying:
... | awk '/Content-Length/ {print $2}'
And last, but not least, your expression for ((i=113;i<=115;i++)) can be also written as for i in {113..115} using brace expansion.
If you want to test a result of the command execution you should put it into $() . So the resulting script should look as follows:
for i in {113..115}; do if [[ $(curl -i -s -k -X 'GET' "http://myurl/some.asp?q=$i" | grep Content-Length | cut -d' ' -f2) != 23 ]]; then echo "good" ; fi; done
Also I've changed a way you iterate the values. {a..b} in bash provides a sequence from 'a' to 'b'.

A script to find all the users who are executing a specific program

I've written the bash script (searchuser) which should display all the users who are executing a specific program or a script (at least a bash script). But when searching for scripts fails because the command the SO is executing is something like bash scriptname.
This script acts parsing the ps command output, it search for all the occurrences of the specified program name, extracts the user and the program name, verifies if the program name is that we're searching for and if it's it displays the relevant information (in this case the user name and the program name, might be better to output also the PID, but that is quite simple). The verification is accomplished to reject all lines containing program names which contain the name of the program but they're not the program we are searching for; if we're searching gedit we don't desire to find sgedit or gedits.
Other issues I've are:
I would like to avoid the use of a tmp file.
I would like to be not tied to GNU extensions.
The script has to be executed as:
root# searchuser programname <invio>
The script searchuser is the following:
#!/bin/bash
i=0
search=$1
tmp=`mktemp`
ps -aux | tr -s ' ' | grep "$search" > $tmp
while read fileline
do
user=`echo "$fileline" | cut -f1 -d' '`
prg=`echo "$fileline" | cut -f11 -d' '`
prg=`basename "$prg"`
if [ "$prg" = "$search" ]; then
echo "$user - $prg"
i=`expr $i + 1`
fi
done < $tmp
if [ $i = 0 ]; then
echo "No users are executing $search"
fi
rm $tmp
exit $i
Have you suggestion about to solve these issues?
One approach might looks like such:
IFS=$'\n' read -r -d '' -a pids < <(pgrep -x -- "$1"; printf '\0')
if (( ! ${#pids[#]} )); then
echo "No users are executing $1"
fi
for pid in "${pids[#]}"; do
# build a more accurate command line than the one ps emits
args=( )
while IFS= read -r -d '' arg; do
args+=( "$arg" )
done </proc/"$pid"/cmdline
(( ${#args[#]} )) || continue # exited while we were running
printf -v cmdline_str '%q ' "${args[#]}"
user=$(stat --format=%U /proc/"$pid") || continue # exited while we were running
printf '%q - %s\n' "$user" "${cmdline_str% }"
done
Unlike the output from ps, which doesn't distinguish between ./command "some argument" and ./command "some" "argument", this will emit output which correctly shows the arguments run by each user, with quoting which will re-run the given command correctly.
What about:
ps -e -o user,comm | egrep "^[^ ]+ +$1$" | cut -d' ' -f1 | sort -u
* Addendum *
This statement:
ps -e -o user,pid,comm | egrep "^\s*\S+\s+\S+\s*$1$" | while read a b; do echo $a; done | sort | uniq -c
or this one:
ps -e -o user,pid,comm | egrep "^\s*\S+\s+\S+\s*sleep$" | xargs -L1 echo | cut -d ' ' -f1 | sort | uniq -c
shows the number of process instances by user.

How to replace or escape <tab> characters with \t in bash script and being able to output single quotes?

In the goal to create a file from a one line (bash) command, the goal is to output the contents of any text file - in this example a bash script - and wrap each line inside a command that is able to output that same line when pasted in a Terminal window.
Example source input file:
Line 1
Line 2
Line 3
Example desired output:
echo 'Line 1';echo 'Line 2';echo 'Line 3';
Note: whether printf, echo or another command is used to create the output, doesn't matter as long as the source is human readable.
One hurdle were the single quotes, that would not be recreated. Therefore use the form $'string', which are treated specially. The word expands to string, with backslash-escaped characters replaced as specified by the ANSI C standard.
Another requirement is to re-create tab characters from the old file in the new file. Therefore the wish is to replace <\tab> characters with \t.
Our tries to do this with sed or tr fail. How to replace tabs with their escape \t counterpart and still being able to output lines with original quotes?
Input file /Library/Scripts/BootRepairMount.sh contains:
$ cat /Library/Scripts/BootRepairMount.sh
#!/bin/bash
sleep 18
for OUTPUT in $(diskutil list | grep ': Apple_HFS' | awk '{ print $NF }')
do
if [[ -z $(df -lnh | grep /dev/$OUTPUT) ]]; then
echo "$OUTPUT is not mounted, repair and mount"
diskutil repairVolume $OUTPUT
diskutil mount $OUTPUT
fi
done
The best shell one line command we could create is:
$ oldifs=$IFS;printf '\n';printf '{';while IFS= read -r p;do [[ "$p" == *"'"* ]] && echo -n "echo $'$p';" || echo -n "echo '$p';"; done < /Library/Scripts/BootRepairMount.sh | tr '\t' '\134\164';printf '}';printf '\n\n';IFS=$oldifs
Which returns this faulty output:
{echo '#!/bin/bash';echo 'sleep 18';echo $'for OUTPUT in $(diskutil list | grep ': Apple_HFS' | awk '{ print $NF }')';echo 'do';echo '\if [[ -z $(df -lnh | grep /dev/$OUTPUT) ]]; then';echo '\\echo "$OUTPUT is not mounted, repair and mount"';echo '\\diskutil repairVolume $OUTPUT';echo '\\diskutil mount $OUTPUT';echo '\fi';echo 'done';}
Desired output is:
{echo '#!/bin/bash';echo 'sleep 18';echo $'for OUTPUT in $(diskutil list | grep ': Apple_HFS' | awk '{ print $NF }')';echo 'do';echo '\tif [[ -z $(df -lnh | grep /dev/$OUTPUT) ]]; then';echo '\t\techo "$OUTPUT is not mounted, repair and mount"';echo '\t\tdiskutil repairVolume $OUTPUT';echo '\t\tdiskutil mount $OUTPUT';echo '\tfi';echo 'done';}
Bash one line command version 2
$ oldifs=$IFS;printf '\n';printf '{';while IFS= read -r p;do [[ "$p" == *"'"* ]] && printf 'printf $'\''%q'\'';' "$p" || printf 'printf '\''%q'\'';' "$p"; done < /Library/Scripts/BootRepairMount.sh;printf '}';printf '\n\n';IFS=$oldifs
returns output that is heavy escaped:
{printf '\#\!/bin/bash';printf 'sleep\ 18';printf $'for\ OUTPUT\ in\ \$\(diskutil\ list\ \|\ grep\ \':\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ Apple_HFS\'\ \|\ awk\ \'\{\ print\ \$NF\ \}\'\)';printf 'do';printf '$'\tif [[ -z $(df -lnh | grep /dev/$OUTPUT) ]]; then'';printf '$'\t\techo "$OUTPUT is not mounted, repair and mount"'';printf '$'\t\tdiskutil repairVolume $OUTPUT'';printf '$'\t\tdiskutil mount $OUTPUT'';printf '$'\tfi'';printf 'done';}
that never gets unescaped back to its original values in Mac OS X 10.7.5.
printf '\#\!/bin/bash';
outputs:
\#\!/bin/bash
As well as:
echo -e '\#\!/bin/bash'
does output the unescaped value
\#\!/bin/bash
-e is not a valid command switch for the Mac OS X 10.7.5 echo command, according to its man page.
bash's builtin command printf has %q format code that handles this:
printf '\n{ '; while IFS= read -r p; do printf "echo %q; " "$p"; done < /Library/Scripts/BootRepairMount.sh; printf '}\n\n'
Unfortunately, it doesn't always choose quoting/escaping modes that're easy to read. Specifically, it tends to prefer escaping individual metacharacters (e.g. spaces) rather than enclosing them in quotes:
{ echo \#\!/bin/bash; echo sleep\ 18; echo for\ OUTPUT\ in\ \$(diskutil\ list\ \|\ grep\ \':\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ Apple_HFS\'\ \|\ awk\ \'{\ print\ \$NF\ }\'); echo do; echo $'\tif [[ -z $(df -lnh | grep /dev/$OUTPUT) ]]; then'; echo $'\t\techo "$OUTPUT is not mounted, repair and mount"'; echo $'\t\tdiskutil repairVolume $OUTPUT'; echo $'\t\tdiskutil mount $OUTPUT'; echo $'\tfi'; echo done; }
If I understand right you want paste one long line to the Terminal.app and want get the "source code" of original script. So, need a script what will generate the one-line script.
Maybe a bit unusual solution, but it is easy and simple.
here is the test script called test.sh (instead of your BootReapirMount.sh)
for i in {1..10}
do
date
done
Here is the generator script mkecho.sh
#!/bin/bash
[[ ! -f "$1" ]] && echo "Need filename" && exit 1
asc=$(gzip < "$1" | base64)
echo "base64 -D <<<'$asc'| gzip -d"
Now, run:
bash mkecho.sh test.sh
you will get the next:
base64 -D <<<'H4sIAASwqFEAA0vLL1LIVMjMU6g21NMzNKjlSsnn4kxJLEkFMvJSuQBZFmY0HwAAAA=='| gzip -d
If you copy and paste the above into the terminal, it will will display the original test.sh
Variant2
If you want directly execute the script, you should modify the mkecho.sh to the next mkeval.sh
#!/bin/bash
[[ ! -f "$1" ]] && echo "Need filename" && exit 1
asc=$(gzip < "$1" | base64)
echo -n 'eval "$(base64 -D <<<"'
echo -n $asc
echo -n '" | gzip -d)"'
echo
When run
bash mkeval.sh test.sh
will get
eval "$(base64 -D <<<"H4sIAASwqFEAA0vLL1LIVMjMU6g21NMzNKjlSsnn4kxJLEkFMvJSuQBZFmY0HwAAAA==" | gzip -d)"
and finally when you copy and paste it into the terminal, you run the test.sh and will get:
Fri May 31 16:25:08 CEST 2013
... 8 lined deleted...
Fri May 31 16:25:08 CEST 2013
Warning: because the script is NOT TESTED for every possible conditions, nor for redirects and so on - I really don't recommending using the eval verision.
sed 's/\\t/\\/g'
$ echo 'ffsd \tif [[ -z $' | sed 's/\\t/\\/g'
ffsd \if [[ -z $

bash script pulling variables from .txt, keeps giving syntax error while trying to use mount command

Ive been trying to get this to work for the last week and cannot figure out why this is not working. I get mixed results typing directly into the terminal, but keep getting syntax error messages when running from the .sh. using ubuntu 11.10
It looks like part of the mount command gets pushed to the next line not allowing it to complete properly.. I have no idea why this is happening or how to prevent it from going to the second line.
i have several lines defined as follows in mounts.txt, that gets read from mount-drives.sh below
I have called it to run using sudo so it shouldnt be a permissions issue.
Thanks for taking a look, let me know if additional info is needed.
mounts.txt
mountname,//server/share$,username,password,
mount-drives.sh ---origional, updated below
#!/bin/bash
while read LINE;
do
# split lines up using , to separate variables
name=$(echo $LINE | cut -d ',' -f 1)
path=$(echo $LINE | cut -d ',' -f 2)
user=$(echo $LINE | cut -d ',' -f 3)
pass=$(echo $LINE | cut -d ',' -f 4)
echo $name
echo $path
echo $user
echo $pass
location="/mnt/test/$name/"
if [ ! -d $location ]
then
mkdir $location
fi
otherstuff="-o rw,uid=1000,gid=1000,file_mode=0777,dir_mode=0777,username=$user,password=$pass"
mount -t cifs $otherstuff $path $location
done < "/path/to/mounts.txt";
mount-drives.sh ---updated
#!/bin/bash
while read LINE
do
name=$(echo $LINE | cut -d ',' -f 1)
path=$(echo $LINE | cut -d ',' -f 2)
user=$(echo $LINE | cut -d ',' -f 3)
pass=$(echo $LINE | cut -d ',' -f 4)
empty=$(echo $LINE | cut -d ',' -f 5)
location="/mount/test/$name/"
if [ ! -d $location ]
then
mkdir $location
fi
mounting="mount -t cifs $path $location -o username=$user,password=$pass,rw,uid=1000,gid=1000,file_mode=0777,dir_mode=0777"
$mounting
echo $mounting >> test.txt
done < "/var/www/MediaCenter/mounts.txt"
Stab in the dark (after reading the comments). The "$pass" is picking up a newline because the mounts.txt was created in windows and has windows line endings. Try changing the echo $pass line to:
echo ---${pass}---
and see if it all shows up correctly.
There's a lot here that could stand improvement. Consider the following -- far more compact, far more correct -- approach:
while IFS=, read -u 3 -r name path user pass empty _; do
mkdir -p "$location"
cmd=( mount \
-t cifs \
-o "rw,uid=1000,gid=1000,file_mode=0777,dir_mode=0777,username=$user,password=$pass" \
"$path" "$location" \
)
printf -v cmd_str '%q ' "${cmd[#]}" # generate a string corresponding with the command
echo "$cmd_str" >>test.txt # append that string to our output file
"${cmd[#]}" # run the command in the array
done 3<mounts.txt
Unlike the original, this will work correctly even if your path or location values contain whitespace.

Resources