Concatenate strings inside bash script (different behaviour from shell) - linux

I'm trying some staff that is working perfectly when I write it in the regular shell, but when I include it in a bash script file, it doesn't.
First example:
m=`date +%m`
m_1=$((m-1))
echo $m_1
This gives me the value of the last month (actual minus one), but doesn't work if its executed from a script.
Second example:
m=6
m=$m"t"
echo m
This returns "6t" in the shell (concatenates $m with "t"), but just gives me "t" when executing from a script.
I assume all these may be answered easily by an experienced Linux user, but I'm just learning as I go.
Thanks in advance.

Re-check your syntax.
Your first code snippet works either from command line, from bash and from sh since your syntax is valid sh. In my opinion you probably have typos in your script file:
~$ m=`date +%m`; m_1=$((m-1)); echo $m_1
4
~$ cat > foo.sh
m=`date +%m`; m_1=$((m-1)); echo $m_1
^C
~$ bash foo.sh
4
~$ sh foo.sh
4
The same can apply to the other snippet with corrections:
~$ m=6; m=$m"t"; echo $m
6t
~$ cat > foo.sh
m=6; m=$m"t"; echo $m
^C
~$ bash foo.sh
6t
~$ sh foo.sh
6t

Make sure the first line of your script is
#!/bin/bash
rather than
#!/bin/sh
Bash will only enable its extended features if explicitly run as bash. If run as sh, it will operate in POSIX compatibility mode.

First of all, it works fine for me in a script, and on the terminal.
Second of all, your last line, echo m will just output "m". I think you meant "$m"..

Related

echo Command of Shell Script -e option writing on file [duplicate]

I have a line of code that works fine in my terminal:
for i in *.mp4; do echo ffmpeg -i "$i" "${i/.mp4/.mp3}"; done
Then I put the exact same line of code in a script myscript.sh:
#!/bin/sh
for i in *.mp4; do echo ffmpeg -i "$i" "${i/.mp4/.mp3}"; done
However, now I get an error when running it:
$ sh myscript.sh
myscript.sh: 2: myscript.sh: Bad substitution
Based on other questions I tried changing the shebang to #!/bin/bash, but I get the exact same error. Why can't I run this script?
TL;DR: Since you are using Bash specific features, your script has to run with Bash and not with sh:
$ sh myscript.sh
myscript.sh: 2: myscript.sh: Bad substitution
$ bash myscript.sh
ffmpeg -i bar.mp4 bar.mp3
ffmpeg -i foo.mp4 foo.mp3
See Difference between sh and Bash. To find out which sh you are using: readlink -f $(which sh).
The best way to ensure a bash specific script always runs correctly
The best practices are to both:
Replace #!/bin/sh with #!/bin/bash (or whichever other shell your script depends on).
Run this script (and all others!) with ./myscript.sh or /path/to/myscript.sh, without a leading sh or bash.
Here's an example:
$ cat myscript.sh
#!/bin/bash
for i in *.mp4
do
echo ffmpeg -i "$i" "${i/.mp4/.mp3}"
done
$ chmod +x myscript.sh # Ensure script is executable
$ ./myscript.sh
ffmpeg -i bar.mp4 bar.mp3
ffmpeg -i foo.mp4 foo.mp3
(Related: Why ./ in front of scripts?)
The meaning of #!/bin/sh
The shebang suggests which shell the system should use to run a script. This allows you to specify #!/usr/bin/python or #!/bin/bash so that you don't have to remember which script is written in what language.
People use #!/bin/sh when they only use a limited set of features (defined by the POSIX standard) for maximum portability. #!/bin/bash is perfectly fine for user scripts that take advantage of useful bash extensions.
/bin/sh is usually symlinked to either a minimal POSIX compliant shell or to a standard shell (e.g. bash). Even in the latter case, #!/bin/sh may fail because bash will run in compatibility mode as explained in the man page:
If bash is invoked with the name sh, it tries to mimic the startup behavior of historical versions of sh as closely as possible, while conforming to the POSIX standard as well.
The meaning of sh myscript.sh
The shebang is only used when you run ./myscript.sh, /path/to/myscript.sh, or when you drop the extension, put the script in a directory in your $PATH, and just run myscript.
If you explicitly specify an interpreter, that interpreter will be used. sh myscript.sh will force it to run with sh, no matter what the shebang says. This is why changing the shebang is not enough by itself.
You should always run the script with its preferred interpreter, so prefer ./myscript.sh or similar whenever you execute any script.
Other suggested changes to your script:
It is considered good practice to quote variables ("$i" instead of $i). Quoted variables will prevent problems if the stored file name contains white space characters.
I like that you use advanced parameter expansion. I suggest to use "${i%.mp4}.mp3" (instead of "${i/.mp4/.mp3}"), since ${parameter%word} only substitutes at the end (for example a file named foo.mp4.backup).
The ${var/x/y/} construct is not POSIX. In your case, where you just remove a string at the end of a variable and tack on another string, the portable POSIX solution is to use
#!/bin/sh
for i in *.mp4; do
ffmpeg -i "$i" "${i%.mp4}.mp3"
done
or even shorter, ffmpeg -i "$i" "${i%4}3".
The definitive dope for these constructs is the chapter on Parameter Expansion for the POSIX shell.

Error when doing string substitution in bash [duplicate]

I have a line of code that works fine in my terminal:
for i in *.mp4; do echo ffmpeg -i "$i" "${i/.mp4/.mp3}"; done
Then I put the exact same line of code in a script myscript.sh:
#!/bin/sh
for i in *.mp4; do echo ffmpeg -i "$i" "${i/.mp4/.mp3}"; done
However, now I get an error when running it:
$ sh myscript.sh
myscript.sh: 2: myscript.sh: Bad substitution
Based on other questions I tried changing the shebang to #!/bin/bash, but I get the exact same error. Why can't I run this script?
TL;DR: Since you are using Bash specific features, your script has to run with Bash and not with sh:
$ sh myscript.sh
myscript.sh: 2: myscript.sh: Bad substitution
$ bash myscript.sh
ffmpeg -i bar.mp4 bar.mp3
ffmpeg -i foo.mp4 foo.mp3
See Difference between sh and Bash. To find out which sh you are using: readlink -f $(which sh).
The best way to ensure a bash specific script always runs correctly
The best practices are to both:
Replace #!/bin/sh with #!/bin/bash (or whichever other shell your script depends on).
Run this script (and all others!) with ./myscript.sh or /path/to/myscript.sh, without a leading sh or bash.
Here's an example:
$ cat myscript.sh
#!/bin/bash
for i in *.mp4
do
echo ffmpeg -i "$i" "${i/.mp4/.mp3}"
done
$ chmod +x myscript.sh # Ensure script is executable
$ ./myscript.sh
ffmpeg -i bar.mp4 bar.mp3
ffmpeg -i foo.mp4 foo.mp3
(Related: Why ./ in front of scripts?)
The meaning of #!/bin/sh
The shebang suggests which shell the system should use to run a script. This allows you to specify #!/usr/bin/python or #!/bin/bash so that you don't have to remember which script is written in what language.
People use #!/bin/sh when they only use a limited set of features (defined by the POSIX standard) for maximum portability. #!/bin/bash is perfectly fine for user scripts that take advantage of useful bash extensions.
/bin/sh is usually symlinked to either a minimal POSIX compliant shell or to a standard shell (e.g. bash). Even in the latter case, #!/bin/sh may fail because bash will run in compatibility mode as explained in the man page:
If bash is invoked with the name sh, it tries to mimic the startup behavior of historical versions of sh as closely as possible, while conforming to the POSIX standard as well.
The meaning of sh myscript.sh
The shebang is only used when you run ./myscript.sh, /path/to/myscript.sh, or when you drop the extension, put the script in a directory in your $PATH, and just run myscript.
If you explicitly specify an interpreter, that interpreter will be used. sh myscript.sh will force it to run with sh, no matter what the shebang says. This is why changing the shebang is not enough by itself.
You should always run the script with its preferred interpreter, so prefer ./myscript.sh or similar whenever you execute any script.
Other suggested changes to your script:
It is considered good practice to quote variables ("$i" instead of $i). Quoted variables will prevent problems if the stored file name contains white space characters.
I like that you use advanced parameter expansion. I suggest to use "${i%.mp4}.mp3" (instead of "${i/.mp4/.mp3}"), since ${parameter%word} only substitutes at the end (for example a file named foo.mp4.backup).
The ${var/x/y/} construct is not POSIX. In your case, where you just remove a string at the end of a variable and tack on another string, the portable POSIX solution is to use
#!/bin/sh
for i in *.mp4; do
ffmpeg -i "$i" "${i%.mp4}.mp3"
done
or even shorter, ffmpeg -i "$i" "${i%4}3".
The definitive dope for these constructs is the chapter on Parameter Expansion for the POSIX shell.

Bash script: Can't set a variable using which

In my terminal,
prog="cat"
name=$(which $prog)
echo $name
prints /bin/cat
But in my script:
pro="$1"
prog=$(which $pro)
echo "pro is $pro"
echo "prog is "$prog""
running scriptname cat prints
pro is cat
prog is
How do I make which work? it should print prog is /bin/cat
which(1) is an external program used to search PATH for an executable. It behaves differently on different systems and you can't rely on a useful exit code; use (from most to least portable) command -v or type -P (to find the path) or hash (to check) instead.
try printf '%s\n' "$PATH" inside your script as well as outside of it. maybe the command you're looking for is not in the PATH used in the script?
That is almost certainly the cause.

Linux: start a script after another has finished

I read the answer for this issue from this link
in Stackoverflow.com. But I am so new in writing shell script that I did something wrong. The following are my scripts:
testscript:
#!/bin/csh -f
pid=$(ps -opid= -C csh testscript1)
while [ -d /proc/$pid ] ; do
sleep 1
done && csh testscript2
exit
testscript1:
#!/bin/csh -f
/usr/bin/firefox
exit
testscript2:
#!/bin/csh -f
echo Done
exit
The purpose is for testscript to call testscript1 first; once testscript1 already finish (which means the firefox called in script1 is closed) testscript will call testscript2. However I got this result after running testscript:
$ csh testscript
Illegal variable name.
Please help me with this issue. Thanks ahead.
I believe this line is not CSH:
pid=$(ps -opid= -C csh testscript1)
In general in csh you define variables like this:
set pid=...
I am not sure what the $() syntax is, perhaps back ticks woudl work as a replacement:
set pid=`ps -opid= -C csh testscript1`
Perhaps you didn't notice that the scripts you found were written for bash, not csh, but
you're trying to process them with the csh interpreter.
It looks like you've misunderstood what the original code was trying to do -- it was
intended to monitor an already-existing process, by looking up its process id using the process name.
You seem to be trying to start the first process from inside the ps command. But
in that case, there's no need for you to do anything so complicated -- all you need
is:
#!/bin/csh
csh testscript1
csh testscript2
Unless you go out of your way to run one of the scripts in the background,
the second script will not run until the first script is finished.
Although this has nothing to do with your problem, csh is more oriented toward
interactive use; for script writing, it's considered a poor choice, so you might be
better off learning bash instead.
Try,
below script will check testscript1's pid, if it is not found then it will execute testscirpt2
sp=$(ps -ef | grep testscript1 | grep -v grep | awk '{print $2}')
/bin/ls -l /proc/ | grep $sp > /dev/null 2>&1 && sleep 0 || /bin/csh testscript2

different behavior when run in nohup mode (linux shell)

I try to do string replacement in linux shell,
str=2011/10/10
echo "$str"
a=${str//\//\_}
echo $a
It can execute when I invoke command : ./test.sh
But if I run it in nohup mode, using command : nohup ./test.sh &
It says that
./test.sh: 8: Bad substitution
What's wrong here ?
Thanks
Since you have no #!/bin/bash at the top of your script, the 'nohup' command is using /bin/sh and your system's /bin/sh isn't BASH. Your first and third lines where you assign 'str' and 'a' are not correct Bourne syntax.
Since you likely want to use BASH and not a shell that uses strict Bourne syntax, you should add a #! line at the top of your script like this:
#!/bin/bash
str=2011/10/10
echo "$str"
a=${str//\//\_}
echo $a

Resources