code:
path=$PATH:
while [ -n $path ]
do
ls -ld ${path%%:*}
path=${path#*:}
done
I want to get the each part of path .When run the script ,it can not get out of the while process 。Please tell me why . Is some problem in 'while [ -n $path ]' ?
The final cut never results in an empty string. If you have a:b:c, you'll strip off the a and then the b, but never the c. I.e., this:
${path#*:}
Will always result in a non-empty string for the last piece of the path. Since the -n check looks for an empty string, your loop runs forever.
If $path doesn't have a colon in it, ${path#*:} will return $path. So you have an infinite loop.
p="foo"
$ echo ${p#*:}
foo
$ p="foo:bar"
$ echo ${p#*:}
bar
You have some bugs in your code. This should do the trick:
path=$PATH
while [[ $path != '' ]]; do
# you can replace echo to whatever you need, like ls -ld
echo ${path%%:*}
if echo $path | grep ':' >/dev/null; then
path=${path#*:}
else path=''
fi
done
Your path, after is initialized, will always check True for [ -n path ] test. This is the main reason for which you never get out of the while loop.
Related
I have following command output
$ /opt/CrowdStrike/falconctl -g --aid | grep 'aid='
aid="fdwe234wfgrgf34tfsf23rwefwef3".
I want to check if there is any string after aid= (inside ""). If there is any string, command return code should be 0 and if no value return code must be !=0.
Can someone please help to extend this command to get required output?
Idea is to make sure my bash script to fail if aid= doesn't has any value.
You can use regex to check whether one or more characters exist inside the double quotes. And, you can use regex capture group to extract that value:
if [[ $(/opt/CrowdStrike/falconctl -g --aid | grep 'aid=') =~ ^aid=\"(.+)\"$ ]]; then
aid=${BASH_REMATCH[0]}
echo "aid is $aid"
else
echo "aid not found"
fi
Note that the regex I use is .+ which means 1 or more characters, since you require the string to be non-empty. This is in contrast of the usual .* regex which would have be 0 or more characters.
I don't have falconctl on my system so to mimic its output I'll use a couple files:
$ head falcon*out
==> falcon.1.out <==
some stuff
aid="fdwe234wfgrgf34tfsf23rwefwef3".
some more stuff
==> falcon.2.out <==
some stuff
aid=""
some more stuff
One grep idea:
grep -Eq '^aid="[^"]+"' <filename>
Where:
-E - enable extended regex support
-q - run in silent/quiet mode (suppress all output)
the return code can be captured from $?
Taking for a test drive:
for fname in falcon*out
do
printf "\n############# %s\n" "$fname"
cat "$fname" | grep -Eq '^aid="[^"]+"' "$fname"
echo "return code: $?"
done
This generates:
############# falcon.1.out
return code: 0
############# falcon.2.out
return code: 1
I want to check if a file contains a specific string or not in bash. I used this script, but it doesn't work:
if [[ 'grep 'SomeString' $File' ]];then
# Some Actions
fi
What's wrong in my code?
if grep -q SomeString "$File"; then
Some Actions # SomeString was found
fi
You don't need [[ ]] here. Just run the command directly. Add -q option when you don't need the string displayed when it was found.
The grep command returns 0 or 1 in the exit code depending on
the result of search. 0 if something was found; 1 otherwise.
$ echo hello | grep hi ; echo $?
1
$ echo hello | grep he ; echo $?
hello
0
$ echo hello | grep -q he ; echo $?
0
You can specify commands as an condition of if. If the command returns 0 in its exitcode that means that the condition is true; otherwise false.
$ if /bin/true; then echo that is true; fi
that is true
$ if /bin/false; then echo that is true; fi
$
As you can see you run here the programs directly. No additional [] or [[]].
In case if you want to check whether file does not contain a specific string, you can do it as follows.
if ! grep -q SomeString "$File"; then
Some Actions # SomeString was not found
fi
In addition to other answers, which told you how to do what you wanted, I try to explain what was wrong (which is what you wanted.
In Bash, if is to be followed with a command. If the exit code of this command is equal to 0, then the then part is executed, else the else part if any is executed.
You can do that with any command as explained in other answers: if /bin/true; then ...; fi
[[ is an internal bash command dedicated to some tests, like file existence, variable comparisons. Similarly [ is an external command (it is located typically in /usr/bin/[) that performs roughly the same tests but needs ] as a final argument, which is why ] must be padded with a space on the left, which is not the case with ]].
Here you needn't [[ nor [.
Another thing is the way you quote things. In bash, there is only one case where pairs of quotes do nest, it is "$(command "argument")". But in 'grep 'SomeString' $File' you have only one word, because 'grep ' is a quoted unit, which is concatenated with SomeString and then again concatenated with ' $File'. The variable $File is not even replaced with its value because of the use of single quotes. The proper way to do that is grep 'SomeString' "$File".
Shortest (correct) version:
grep -q "something" file; [ $? -eq 0 ] && echo "yes" || echo "no"
can be also written as
grep -q "something" file; test $? -eq 0 && echo "yes" || echo "no"
but you dont need to explicitly test it in this case, so the same with:
grep -q "something" file && echo "yes" || echo "no"
##To check for a particular string in a file
cd PATH_TO_YOUR_DIRECTORY #Changing directory to your working directory
File=YOUR_FILENAME
if grep -q STRING_YOU_ARE_CHECKING_FOR "$File"; ##note the space after the string you are searching for
then
echo "Hooray!!It's available"
else
echo "Oops!!Not available"
fi
grep -q [PATTERN] [FILE] && echo $?
The exit status is 0 (true) if the pattern was found; otherwise blankstring.
if grep -q [string] [filename]
then
[whatever action]
fi
Example
if grep -q 'my cat is in a tree' /tmp/cat.txt
then
mkdir cat
fi
In case you want to checkif the string matches the whole line and if it is a fixed string, You can do it this way
grep -Fxq [String] [filePath]
example
searchString="Hello World"
file="./test.log"
if grep -Fxq "$searchString" $file
then
echo "String found in $file"
else
echo "String not found in $file"
fi
From the man file:
-F, --fixed-strings
Interpret PATTERN as a list of fixed strings, separated by newlines, any of
which is to be matched.
(-F is specified by POSIX.)
-x, --line-regexp
Select only those matches that exactly match the whole line. (-x is specified by
POSIX.)
-q, --quiet, --silent
Quiet; do not write anything to standard output. Exit immediately with zero
status if any match is
found, even if an error was detected. Also see the -s or --no-messages
option. (-q is specified by
POSIX.)
Try this:
if [[ $(grep "SomeString" $File) ]] ; then
echo "Found"
else
echo "Not Found"
fi
I done this, seems to work fine
if grep $SearchTerm $FileToSearch; then
echo "$SearchTerm found OK"
else
echo "$SearchTerm not found"
fi
grep -q "something" file
[[ !? -eq 0 ]] && echo "yes" || echo "no"
I am looking into how a particular exploit works, and I chose to look at one in the program 'chkrootkit' which allows for any user to run a malicious file as root. The source code for this vulnerable shellscript is as follows
slapper (){
SLAPPER_FILES="${ROOTDIR}tmp/.bugtraq ${ROOTDIR}tmp/.bugtraq.c"
SLAPPER_FILES="$SLAPPER_FILES ${ROOTDIR}tmp/.unlock ${ROOTDIR}tmp/httpd \
${ROOTDIR}tmp/update ${ROOTDIR}tmp/.cinik ${ROOTDIR}tmp/.b"a
SLAPPER_PORT="0.0:2002 |0.0:4156 |0.0:1978 |0.0:1812 |0.0:2015 "
OPT=-an
STATUS=0
file_port=
if ${netstat} "${OPT}"|${egrep} "^tcp"|${egrep} "${SLAPPER_PORT}">
/dev/null 2>&1
then
STATUS=1
[ "$SYSTEM" = "Linux" ] && file_port=`netstat -p ${OPT} | \
$egrep ^tcp|$egrep "${SLAPPER_PORT}" | ${awk} '{ print $7 }' |
tr -d :`
fi
for i in ${SLAPPER_FILES}; do
if [ -f ${i} ]; then
file_port=$file_port $i
STATUS=1
fi
done
if [ ${STATUS} -eq 1 ] ;then
echo "Warning: Possible Slapper Worm installed ($file_port)"
else
if [ "${QUIET}" != "t" ]; then echo "not infected"; fi
return ${NOT_INFECTED}
fi
}
I know that the reason the exploit works is because the line 'file_port=$file_port $i' will execute all files specified in $SLAPPER_FILES as the user chkrootkit is running (usually root), if $file_port is empty, because of missing quotation marks around the
variable assignment."
My question is why does the command
file_port=$file_port $i
result in execution of the file? Assuming that $i refers to the path of the file (/tmp/update)
I can see that file_port might be changed to some long netstat command in the previous if statement, is this something to do with it?
I've been trying to get my head around this all day to no avail, so at this point any help will be greatly appreciated :)
This is the one-shot variable assignment feature of any Bourne shell. Any command can be prefixed with zero or more variable assignments:
VAR1=VALUE1 VAR2=VALUE2 command arguments ...
Runs command arguments ... with the respective environment variables set for just this command. A typical use might be
EDITOR=vim crontab -e
I found some strange thing in bash and I can't understand how it works.
[test ~]$ a=""
[test ~]$ $a && echo 1
1
[test ~]$ $a
[test ~]$ echo $?
0
Why does $a (which is empty) return 0? Is it somehow transformed to empty command?
If I add quotes or write empty string before &&, it will return error. While empty command returns 0.
[test ~]$ "$a" && echo 1
-bash: : command not found
[test ~]$ "" && echo 1
-bash: : command not found
[test ~]$ `` && echo 1
1
So, what is happening when I type $a?
You seem to confuse bash with some other programming language. Variables get replaced, then what is left gets executed.
"$a"
This is the content of a, between quotation marks. a is empty, so this is equivalent to:
""
That is not a command. "Command not found." As there was an error, the execution was not successful (shell return code is not 0), so the second half of the command -- && echo 1 -- does not get executed.
Backticks...
``
...execute whatever is between them, with the output of that command replacing the whole construct. (There is also $() which does the same, and is less prone to being overlooked in a script.) So...
`echo "foo"`
...would evaluate to...
foo
...which would then be executed. So your...
``
...evaluates to...
<empty>
...which is then "executed successfully" (since there is no error).
If you want to test the contents of a, and execute echo 1 only if a is not empty, you should use the test command:
test -n "$a" && echo 1
There is a convenient alias for test, which is [, which also conveniently ignores a trailing ]...
[ -n "$a" ] && echo 1
...and a bash-ism [[ that "knows" about variable replacement and thus does not need quotation marks to avoid complaining about a missing argument if $a does indeed evaluate to empty...
[[ -n $a ]] && echo 1
...or, of course, the more verbose...
if [[ -n $a ]]
then
echo 1
fi
Ah. Missed the core part of the question:
$a && echo 1
This is two statements, separated by &&. The second statement only gets executed if the first one executes OK. The bash takes the line apart and executes the first statement:
$a
This is...
<empty>
...which is "successful", so the second statement gets executed. Opposed to that...
&& echo 1
...is a syntax error because there is no first statement. ;-) (Tricky, I know, but that's the way this cookie crumbles.)
a=""
or
a=" " #a long empty string
then
$> $a
will return 0
$> $noExistVar
will also return 0.
They get "executed", in fact, nothing gets executed. same as you press enter or pressing 10 spaces then enter, you get return code 0 too.
$> && echo 1
this will fail, because bash will try to execute the first part, in this case it is missing.
$> $notExistVar && echo 1
Here it works, I guess bash found the first part the $whatever, therefore no syntax error. Then "execute" it, well nothing to execute, return 0, (same as pressing enter after prompt), then check, if first part returned 0, exec the cmd after &&.
I said guess because I didn't check bash's source codes. Please correct me if it is wrong.
the $> " " && echo 1 case, I think it is clear, don't need to explain.
Hi but it appears that if my strings have spaces in it, it won't work properly. My entire script is here:
#!/bin/bash
echo $#; echo $#
MoveToTarget() {
#This takes to 2 arguments: source and target
echo ""$1" "$2""
cp -rf "$1"/* "$2"
rm -r "$1"
}
WaitForProcessToEnd() {
#This takes 1 argument. The PID to wait for
#Unlike the AutoIt version, this sleeps 1 second
while [ $(kill -0 "$1") ]; do
sleep 1
done
}
RunApplication() {
#This takes 1 application, the path to the thing to execute
open "$1"
}
#our main code block
pid="$1"
SourcePath="$2"
DestPath="$3"
ToExecute="$4"
WaitForProcessToEnd $pid
MoveToTarget "$SourcePath" "$DestPath"
RunApplication "$ToExecute"
exit
Note that I have tried the variables like $DestPath with and without quotes around them, with no luck. This code gets run with a Python script, and when the arguments are passed, quotes are around them. I appreciate any help!
Edit: (Python script)
bootstrapper_command = r'"%s" "%s" "%s" "%s" "%s"' % (bootstrapper_path, os.getpid(), extracted_path, self.app_path, self.postexecute)
shell = True
subprocess.Popen(bootstrapper_command, shell=shell)
Bash quotes are syntactic, not literal. Greg's Wiki, as usual, has the most excellent explanation you could wish for.
Try removing the *, it isn't needed for recursive copy.
cp -rf "$1"/* "$2"
to:
cp -rf "$1/" "$2"
I think globbing was ruining your quoting that was protecting you from spaces in filenames.