BASH: if statement execute command and function - bash

I've run into an issue in which I think should be easy to resolve, but for the life of me, I can't figure it out. Could be that it's really late; not sure.
So I have a shell script and I have an if statement that I need to run. The problem is that I have a function inside this bash script that I am using to actually build part of this find command inside the if statement. I want to know how I can do both, without receiving an error [: too many arguments.
Here's the current code:
if [ -n `find ./ `build_ext_names`` ];then
That's all I really need to post. I need to figure out how to run that build_ext_names inside that find command, which in-turn is inside the ifstatement

Michael Aaron Safyan has the right idea, but to fix the immediate problem you can just use the simpler $(command) construct instead of ```command` `` for command substitution. It allows for much simpler nesting:
if [ -n "$(find ./ "$(build_ext_names)")" ]; then

This is easier if you split it up:
function whateverItIsYouAreTryingToDo() {
local ext_names=$(build_ext_names)
local find_result=$(find ./ $ext_names)
if [ -n "$find_result" ] ; then
# Contents of if...
fi
}

Related

Why is executing a ruby command on bash to command file not working?

Code from .command file:
cd "$(dirname "$0")"
g1=Hi-Lo
echo Welcome to Ruby_Games! So far, you can play $g1.
echo What game would you like to play?
read game_choice
if [$game_choice == $g1]
then
ruby Hi-Lo.rb
fi
Output:
Welcome to Ruby_Games! So far, you can play Hi-Lo.
What game would you like to play?
Hi-Lo
/Users/Abbas/Desktop/Ruby_Games/LAUNCHER.command: line 6: [Hi-Lo: command not found
logout
So what exactly is going wrong? Thanks
I believe you need double quotes in your if statement
Similar to example 6.4 here: http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-6.html
cd "$(dirname "$0")"
g1=Hi-Lo
echo Welcome to Ruby_Games! So far, you can play $g1.
echo What game would you like to play?
read game_choice
if [ "$game_choice" = "$g1" ]
then
ruby Hi-Lo.rb
fi
You need spaces between each element of the test (aka [) command. That is, you need a space between [ and $game_choice, between $game_choice and =, etc. Also, as #GregHNZ pointed out, you should use double-quotes around variable references, in case they contain spaces or certain other shell metacharacters. Finally, == in a test expression is a bash extension; use = instead, and it'll work in more basic shells as well. Result:
if [ "$game_choice" = "$g1" ]
Spaces are important delimiters in shell syntax; there are places they're required and places they're forbidden, and very very few places where they're optional. In many languages, you can add or remove spaces to make the code more readable, but that's not the case in shell.
BTW, I recommend using shellcheck.net; it does a pretty good job of spotting errors like this. Actually, it points out a couple I didn't think of: you should add a shebang line to the beginning of the script, and using cd without checking for an error risks the rest of the script running in an unexpected directory. So you should use something like this:
#!/bin/bash
cd "$(dirname "$0")" || {
echo "Error cd'ing to the script's directory" >&2
exit 1
}

Can't add another script in bash

I wrote a bash script which calls another script in the same folder. I am doing this by simply putting ./email_pacotes.sh in the main script
#awk '{print $2}' >> /tmp/lista_pacotes.log adiciona resultado ao arquivo /tmp/tmp_pacotes_adicionados.log
echo "\nPacotes adicionados até" $(date) "\n" >> /tmp/tmp_pacotes_adicionados.log
cat /tmp/diferencas.log >> /tmp/tmp_pacotes_adicionados.log
./email_pacotes.sh
#adiciona resultados anteriores
cat /tmp/pacotes_adicionados.log >> /tmp/tmp_pacotes_adicionados.log
I thought that it was working correctly, but I had to debug the script for other reasons and I found out it wasn't adding the second script the first time a run the main script.
I was getting the following message:
[...]
./email_pacotes.sh: 17: ./email_pacotes.sh: [[: not found
./email_pacotes.sh: 17: ./email_pacotes.sh: [[: not found
./email_pacotes.sh: 17: ./email_pacotes.sh: [[: not found
./email_pacotes.sh: 17: ./email_pacotes.sh: [[: not found
./email_pacotes.sh: 17: ./email_pacotes.sh: [[: not found
[...]
This happens when I run the script the first time I put it in a folder. If I run it again, the message doesn't show anymore, so I guessing it is not a problem with the syntax. I also thought could be something with permissions, but I changed both scripts to 0777 and the message persists.
Is this a normal behaviour? What could be causing this?
Obs1: I am debugging the main script using the -x option.
Obs2: I made another test now. It keeps throwing the same message, but at certain point it finally calls the script. So maybe is just the time to find the file or throw a exception?
From the error, I'm pretty sure you're running the second script with a shell other than bash. The [[ ]] conditional is supported by bash (and zsh and maybe some other shells), but is not standard and there are other shells that don't support it. So if you want to use that (or any other nonstandard bash features), you need to use a proper shebang line in that script. Generally, that means you need to start the script with #!/bin/bash (or maybe #!/usr/bin/env bash), not with #!/bin/sh.
There's another thing that worries me, though. Running the second script with ./email_pacotes.sh will look for it in the current working directory, which is inherited from the process that ran the first script, and could be pretty much anywhere. If you want it to look for the second script in the same directory the first script is in, the best way is to locate the first script with something like "$(dirname "$BASH_SOURCE")" (and guess what -- BASH_SOURCE is a bash-only feature, so start the first script with a bash shebang as well). Then you can either refer to the second script (and any other relevant files) by explicit path:
#!/bin/bash
...
scriptDir="$(dirname "$BASH_SOURCE")"
if [[ ! -d "$scriptDir" ]]; then
echo "Something's terribly wrong; I can't find myself!" >&2
exit 1
fi
...
"$scriptDir/email_pacotes.sh"
or have the script cd to its own directory and then use relative paths:
#!/bin/bash
...
cd "$(dirname "$BASH_SOURCE")" || {
echo "Something's terribly wrong; I can't cd to my own directory!" >&2
exit 1
}
...
./email_pacotes.sh
I prefer the first approach, because if the any of the scripts accepts paths (e.g. as arguments), the user will expect those paths to be interpreted relative to where the user was when they ran the script, not relative to where the script itself is; cding in the script will break this.

Can't check checksum in bash --- apparently, wrong syntax?

I have this bit of code, that is supposed to call reload if current file ($1) is changed:
thehash="`cksum $1`"
while true
do
curhash="`cksum $1`"
if "$curhash" -ne "$thehash"; then
reload
fi
...
done
tl;dr: it doesn't work.
Since I am not very experienced with bash, I can't figure out what did I do wrong. I get this error:
58003725 834183 main.pdf: command not found
Apparently, bash is trying to execute curhash? How do I fix this?
You need brackets around your condition in if or to use the test command, so it should be
if [[ "$curhash" != "$thehash" ]]; then
and note that -ne is for integer comparison, != is for string comparison
Without the [[ or test the variable gets expanded and that becomes a command to run, which is why it was trying to execute the output of cksum: the content of curhash was being treated as a command.
Also, as #Sundeep mentioned the more often preferred way to get the output from the subshell is to use $(...) instead of the backticks. here is a good answer talking about that

What is [[: not found error?

I tried to execute a file containing a shell script.
I get an error called "[[: not found" error at the last line. How to resolve it?
[[ is bash. sh wants the [ variant.
Either change that to /usr/bin/bash (or wherever bash is located on your system), or adjust the expression accordingly:
if [ status_of_job -eq 0 ];
[ is actually an executable in linux. but [[ is not.
Try
if [ status_of_job -eq 0 ]; then
(note the single [] set).
This interpreter:
#!/usr/bin/sh
Is either not bash or your file doesn't have the shebang in the right place.
ls -l /usr/bin/sh will tell you if it's a symlink to something other than bash.
If it is bash, then check that there's no leading characters before the #!.
You may find you stumble at other blocks later with POSIX shell related issues. People don't really understand how much bash actually provides until it's taken away. Take a look at this so you can hopefully avoid any other issues:
http://pubs.opengroup.org/onlinepubs/7908799/xcu/shellix.html

Why can I execute a command in a terminal window just fine but encounter an error in a bash script?

I've been organizing my music library as of late, and I'm trying to make everything "look" the same. All my songs should look like 04 John Barleycorn.m4a, but some of them, coming from multi-disk sets, look like 2-04 John Barleycorn.m4a. So I immediately thought, "Why not make a bash script to do all of this tedious work for me?" Little did I know, I would spend more time trying to figure out this "bug" than it would take to just do it by hand. Only one small difference: I wouldn't learn anything doing it by hand!
So here's my script:
#!/bin/bash
filename="/tmp/fileout.txt"
find . -name '?-*.???' > $filename
cat $filename | while read line
do
echo ${line:1}
newname=$(echo ${line%\/*}/${line#*-})
echo $newname
#mv \"$line\" \"$newname\"
done
It should be simple enough, right? It finds all the files with the multi-disk format, and puts them in a text file. Each line is then read back, reformated, and is "moved" to its new location/file name. (some parts are commented out since I want to make sure things "looked" good before moving files) However, when I first tried it out (after things "looked" good and removed the # in front of mv), I kept getting
mv: target `Barleycorn.m4a"' is not a directory
and I think that's because the spaces are not being escaped. I thought by putting quotes around it would solve it, but apparently not.
But I'll try to fix that later. Here's my buggy issue. I want to remove the first character (a period) in the file name (just an example...I don't really need to do this for any reason):
line="./Traffic/Smiling Phases/04 John Barleycorn.m4a"
echo ${line:1}
works just fine by typing that in command-line.
But in a bash script, it responds with:
/home/kyleowen/filerenamer.sh: 15: Bad substitution
I've gotten this error many times before when using ${var//foo/bar/} and other string operations within curly braces.
Why is it doing this? Doesn't my script effectively run all operations as if they were in command-line?
I would love a working bash script, sure...but I'm mainly asking why I'm getting a Bad substitution error when working with string operations. Thanks!
EDIT: I found my quite embarrassing mistake...never did I mention how I was executing these scripts. I was executing them as sh test.sh instead of bash test.sh. I assumed sh would execute them as your user's default shell, but I guess I'm wrong (or the default shell is not bash).
Thanks for the tips on input redirection! I'll post back what I have when I get something that works.
There are a number of quoting inconsistencies:
while read line
do
echo "${line:1}"
newname="${line%\/*}/${line#*-}"
echo "$newname"
# mv "$line" "$newname"
done < <(find . -name '?-*.???')
In general advice: use input redirection instead of piping into read
Reason: the while loop woulld execute in a subshell due to the pipe
Try it without the backslashes in the mv command line?
mv "$line" "$newname"
The backslashes makes mv look for files with literal double quotes in the filename.

Resources