I just started learning PHP. I'm following phpacademy's tutorials which I would recommend to anyone. Anyways, I'm using XAMPP to test out my scripts. I'm trying to write a bash script that will start XAMPP and then open firefox to the localhost page if it finds a specific string, "XAMPP for Linux started.", that has been redirected from the terminal to the file xampp.log. I'm having a problem searching the file. I keep getting a:
grep: for: No such file or directory
I know the file exists, I think my syntax is wrong. This is what I've got so far:
loaded=$false
string="XAMPP for Linux started."
echo "Starting Xampp..."
sudo /opt/lampp/lampp start 2>&1 > ~/Documents/xampp.log
sleep 15
if grep -q $string ~/Documents/xampp.log; then
$loaded=$true
echo -e "\nXampp successfully started!"
fi
if [$loaded -eq $true]; then
echo -e "Opening localhost..."
firefox "http://localhost/"
else
echo -e "\nXampp failed to start."
echo -e "\nHere's what went wrong:\n"
cat ~/Documents/xampp.log
fi
In shell scripts you shouldn't write $variable, since that will do word expansion on the variable's value. In your case, it results in four words.
Always use quotes around the variables, like this:
grep -e "$string" file...
The -e is necessary when the string might start with a dash, and the quotes around the string keep it as one word.
By the way: when you write shell programs, the first line should be set -eu. This enables *e*rror checking and checks for *u*ndefined variables, which will be useful in your case. For more details, read the Bash manual.
You are searching for a string you should put wihtin quotes.
Try "$string" instead of $string
There are a couple of problems:
quote variables if you want to pass them as a simple argument "$string"
there is no $true and $false
bash does variable expansion, it substitutes variable names with their values before executing the command. $loaded=$true should be loaded=true.
you need spaces and usually quotes in the if: if [$loaded -eq $true] if [ "$loaded" -eq true ]. in this case the variable is set so it won't cause problems but in general don't rely on that.
Related
Below is a snapshot of a file called ".bashrc":
I'm beginner in bash and What i'm trying to do in bash is to check if the last two lines inside the file exist and correctly written like for example :
if [ export PATH=/opt/ads2/arm-linux64/bin:$PATH ]
then
echo "found system variable lines"
else
echo "systemvariables do not exists, please insert it in .bashrc"
fi
However, this doesn't seem to be trivial since the tow lines to be shared are not pure string lines.
Thanks in advance
Use grep to find stuff in file contents.
# if file .bashrc contains the line exactly export PATH=....
if grep -Fxq 'export PATH=/opt/ads2/arm-linux64/bin:$PATH' .bashrc ; then
echo "found system variable lines"
else
echo "systemvariables do not exists, please insert it in .bashrc"
fi
Read man grep and decide if you want or not the -F and -x options in grep. For sure research and learn regex - I recommend regex crosswords available on the net. Research also difference between single quoting and double quoting in shell. Remember to check scripts with http://shellcheck.net
I have a bit of an issue and i've tried several ways to fix this but i can't seem to.
So i have two shell scripts.
background.sh: This runs a given command in the background and redirect's output.
#!/bin/bash
if test -t 1; then
exec 1>/dev/null
fi
if test -t 2; then
exec 2>/dev/null
fi
"$#" &
main.sh: This file simply starts the emulator (genymotion) as a background process.
#!/bin/bash
GENY_DIR="/home/user/Documents/MyScript/watchdog/genymotion"
BK="$GENY_DIR/background.sh"
DEVICE="164e959b-0e15-443f-b1fd-26d101edb4a5"
CMD="$BK player --vm-name $DEVICE"
$CMD
This works fine when i have NO spaces in my directory. However, when i try to do: GENY_DIR="home/user/Documents/My Script/watchdog/genymotion"
which i have no choice at the moment. I get an error saying that the file or directory cannot be found. I tried to put "$CMD" in quote but it didn't work.
You can test this by trying to run anything as a background process, doesn't have to be an emulator.
Any advice or feedback would be appreciated. I also tried to do.
BK="'$BK'"
or
BK="\"$BK\""
or
BK=$( echo "$BK" | sed 's/ /\\ /g' )
Don't try to store commands in strings. Use arrays instead:
#!/bin/bash
GENY_DIR="$HOME/Documents/My Script/watchdog/genymotion"
BK="$GENY_DIR/background.sh"
DEVICE="164e959b-0e15-443f-b1fd-26d101edb4a5"
CMD=( "$BK" "player" --vm-name "$DEVICE" )
"${CMD[#]}"
Arrays properly preserve your word boundaries, so that one argument with spaces remains one argument with spaces.
Due to the way word splitting works, adding a literal backslash in front of or quotes around the space will not have a useful effect.
John1024 suggests a good source for additional reading: I'm trying to put a command in a variable, but the complex cases always fail!
try this:
GENY_DIR="home/user/Documents/My\ Script/watchdog/genymotion"
You can escape the space with a backslash.
Hi… Need a little help here…
I tried to emulate the DOS' dir command in Linux using bash script. Basically it's just a wrapped ls command with some parameters plus summary info. Here's the script:
#!/bin/bash
# default to current folder
if [ -z "$1" ]; then var=.;
else var="$1"; fi
# check file existence
if [ -a "$var" ]; then
# list contents with color, folder first
CMD="ls -lgG $var --color --group-directories-first"; $CMD;
# sum all files size
size=$(ls -lgGp "$var" | grep -v / | awk '{ sum += $3 }; END { print sum }')
if [ "$size" == "" ]; then size="0"; fi
# create summary
if [ -d "$var" ]; then
folder=$(find $var/* -maxdepth 0 -type d | wc -l)
file=$(find $var/* -maxdepth 0 -type f | wc -l)
echo "Found: $folder folders "
echo " $file files $size bytes"
fi
# error message
else
echo "dir: Error \"$var\": No such file or directory"
fi
The problem is when the argument contains an asterisk (*), the ls within the script acts differently compare to the direct ls command given at the prompt. Instead of return the whole files list, the script only returns the first file. See the video below to see the comparation in action. I don't know why it behaves like that.
Anyone knows how to fix it? Thank you.
Video: problem in action
UPDATE:
The problem has been solved. Thank you all for the answers. Now my script works as expected. See the video here: http://i.giphy.com/3o8dp1YLz4fIyCbOAU.gif
The asterisk * is expanded by the shell when it parses the command line. In other words, your script doesn't get a parameter containing an asterisk, it gets a list of files as arguments. Your script only works with $1, the first argument. It should work with "$#" instead.
This is because when you retrieve $1 you assume the shell does NOT expand *.
In fact, when * (or other glob) matches, it is expanded, and broken into segments by $IFS, and then passed as $1, $2, etc.
You're lucky if you simply retrieved the first file. When your first file's path contains spaces, you'll get an error because you only get the first segment before the space.
Seriously, read this and especially this. Really.
And please don't do things like
CMD=whatever you get from user input; $CMD;
You are begging for trouble. Don't execute arbitrary string from the user.
Both above answers already answered your question. So, i'm going a bit more verbose.
In your terminal is running the bash interpreter (probably). This is the program which parses your input line(s) and doing "things" based on your input.
When you enter some line the bash start doing the following workflow:
parsing and lexical analysis
expansion
brace expansion
tidle expansion
variable expansion
artithmetic and other substitutions
command substitution
word splitting
filename generation (globbing)
removing quotes
Only after all above the bash
will execute some external commands, like ls or dir.sh... etc.,
or will do so some "internal" actions for the known keywords and builtins like echo, for, if etc...
As you can see, the second last is the filename generation (globbing). So, in your case - if the test* matches some files, your bash expands the willcard characters (aka does the globbing).
So,
when you enter dir.sh test*,
and the test* matches some files
the bash does the expansion first
and after will execute the command dir.sh with already expanded filenames
e.g. the script get executed (in your case) as: dir.sh test.pas test.swift
BTW, it acts exactly with the same way for your ls example:
the bash expands the ls test* to ls test.pas test.swift
then executes the ls with the above two arguments
and the ls will print the result for the got two arguments.
with other words, the ls don't even see the test* argument - if it is possible - the bash expands the wilcard characters. (* and ?).
Now back to your script: add after the shebang the following line:
echo "the $0 got this arguments: $#"
and you will immediatelly see, the real argumemts how your script got executed.
also, in such cases is a good practice trying to execute the script in debug-mode, e.g.
bash -x dir.sh test*
and you will see, what the script does exactly.
Also, you can do the same for your current interpreter, e.g. just enter into the terminal
set -x
and try run the dir.sh test* = and you will see, how the bash will execute the dir.sh command. (to stop the debug mode, just enter set +x)
Everbody is giving you valuable advice which you should definitely should follow!
But here is the real answer to your question.
To pass unexpanded arguments to any executable you need to single quote them:
./your_script '*'
The best solution I have is to use the eval command, in this way:
#!/bin/bash
cmd="some command \"with_quetes_and_asterisk_in_it*\""
echo "$cmd"
eval $cmd
The eval command takes its arguments and evaluates them into the command as the shell does.
This solves my problem when I need to call a command with asterisk '*' in it from a script.
Is it possible to execute node.js app from .sh script, return some variable and continue the .sh script?
Something like:
#!/bin/sh
SOME_VARIABLE = node app.js
echo ${SOME_VARIABLE}
Firstly, ensure you're using bash, not sh, as there are significant differences in functionality between the two.
One simple solution is command substitution, although be aware that trailing newlines in the command output will be stripped. Also, when echoing, to protect the contents of the variable (such as spaces and glob characters) from metaprocessing by the shell, you have to double-quote it:
#!/bin/bash
output=$(node app.js);
echo "$output";
Another solution is process substitution in more recent versions of bash. You could even collect the output as an array of lines in this case:
#!/bin/bash
exec 3< <(node app.js);
lines=();
while read -r; do lines+=("$REPLY"); done <&3;
exec 3<&-;
echo "${lines[#]}";
I'm new to shell programming. I intend to get directory name after zip file was extracted. The print statement of it is
$test.sh helloworld.zip
helloworld
Let's take a look at test.sh:
#! /bin/sh
length=echo `expr index "$1" .zip`
a=$1
echo $(a:0:length}
However I got the Bad substitution error from the compiler.
And when I mention about 'shell'.I just talking about shell for I don't know the difference between bash or the others.I just using Ubuntu 10.04 and using the terminal. (I am using bash.)
If your shell is a sufficiently recent version of bash, that parameter expansion notation should work.
In many other shells, it will not work, and a bad substitution error is the way the shell says 'You asked for a parameter substitution but it does not make sense to me'.
Also, given the script:
#! /bin/sh
length=echo `expr index "$1" .zip`
a=$1
echo $(a:0:length}
The second line exports variable length with value echo for the command that is generated by running expr index "$1" .zip. It does not assign to length. That should be just:
length=$(expr index "${1:?}" .zip)
where the ${1:?} notation generates an error if $1 is not set (if the script is invoked with no arguments).
The last line should be:
echo ${a:0:$length}
Note that if $1 holds filename.zip, the output of expr index $1 .zip is 2, because the letter i appears at index 2 in filename.zip. If the intention is to get the base name of the file without the .zip extension, then the classic way to do it is:
base=$(basename $1 .zip)
and the more modern way is:
base=${1%.zip}
There is a difference; if the name is /path/to/filename.zip, the classic output is filename and the modern one is /path/to/filename. You can get the classic output with:
base=${1%.zip}
base=${base##*/}
Or, in the classic version, you can get the path with:
base=$(dirname $1)/$(basename $1 .zip)`.)
If the file names can contain spaces, you need to think about using double quotes, especially in the invocations of basename and dirname.
Try running it with bash.
bash test.sh helloworld.zip
-likewise-
"try changing the first line to #!/bin/bash" as comment-answered by – #shellter
Try that in bash :
echo $1
len=$(wc -c <<< "$1")
a="${1}.zip"
echo ${a:0:$len}
Adapt it to fit your needs.