Bash wildcard working in command line but not script - bash

I've a basic ls command I'm running in my command line $ ls [root]/*, from which I can see the contents of my $root directory. But, when I run the same command in a script, I see this error: ls: [root]/*: No such file or directory. I'm going to paste the script below, but is someone able to tell why the command is running fine in the command line but not the script? Thank you.
#! /bin/bash
root="[root]"
ls "$root/*"
edit:
I tracked down the problem. The wildcard should not be inside the double quotes unless I'm looking for a file or directory with that name. The script below runs successfully.
#! /bin/bash
root="[root]"
ls "$root/"*

The best answer to my question was the edit I made to my question. I'll say it again.
I wanted to use an ls wildcard in a bash script and wrote this file:
#! /bin/bash
root="[root]"
ls "$root/*"
But, writing my ls this way looks for files or directories with the name *. In order to make use of the wildcard I need to leave * outside of the quotes. See the example below:
#! /bin/bash
root="[root]"
ls "$root/"*
note:
I'm using the square brackets [] in my question and answer contextually. The name of my directory isn't the literal string [root], it's something else, but the square brackets let the reader understand what's being said contextually.
But maybe a fake root path or name is better in the future.

There's a few issues here.
[root] is a shell globbing pattern that matches any of the three characters r, o or t. If you do ls [root] and you happen to have a directory or file called r, o or t, then that pattern would expand to the name of that file or directory.
To safely list the content of a directory whose real name is [root], you would need to quote the name, which brings us to the next point...
A shell globbing pattern does not expand in quotes. This means that "[root]/*" refers to something called *, literally, in the directory [root].
Your script would have to look like
#!/bin/sh
root='[root]'
ls "$root"/*

The existent answers do not solve the problem if you use complex patterns like this:
cp release/artifacts/${OS}/binary?(.exe) $DEST
Because it failed with:
line 6: syntax error near unexpected token `('
To solve that you should add the following option to the script:
#!/usr/bin/bash
shopt -s extglob

Related

bash for loop in python crashed [duplicate]

I am a beginner of bash. I encounter a problem like this:
$ "make -p"
when I type the above in bash command line, there is nothing to happen, no error, no result msg.
I have searched double quotes syntax of bash in many websites. All of these materials give similar interpretation as below:
https://www.gnu.org/software/bash/manual/html_node/Double-Quotes.html
and give examples like:
echo "argument"
I do not find something like "echo argument". Moreover, I find a strange difference between bash command line and bash scripts.
If I type a non-existing command in command line:
$ "holy shit"
$ "look that"
there is nothing to happen. But if I type it in bash scripts:
#!/bin/bash
"holy shit"
"look that"
and execute this script, an error msg will be throw out:
$ ./myshell
./myshell: line 2: holy shit: command not found
./myshell: line 3: look that: command not found
Would someone can help give a detailed interpretation about the effect of double quotes when they enclosed the whole command?
Why there is no output in command-line?
Why it is different between command line and scripts?
If you enter a command foo, the shell searches the directories listed in your PATH variable until it finds a command of this name. If there is none, you get the error message command not found.
If you enter a command, which contains at least one slash - for example ./foo or foo/bar -, the shell does not search the PATH, but assumes that you have already entered the correct path to your command. If it does not exist, you get the error message No such file or directory.
In your case,
"cd home"
searches for a file with name cd home somewhere along your PATH, but there is no file of this name, and you get command not found. If you enter
"cd /home"
the shell bypasses PATH-search and assumes, that there exists a directory named cd (i.e. the 3 letters c,d,space) in your current directory, and below it a file named home, with x-bit set. There is no such file (and no such directory) on your system, and you get the error message No such file or directory.
If you are in the mood of experimenting around, you could try the following:
mydir="cd "
mkdir "$mydir"
echo "echo Hello Stranger" >"$mydir/home"
chmod +x "$mydir/home"
"cd /home"
This should print Hello Stranger. Pay attention that in the assignment to mydir, there must be a single space between the cd and the closing quote.
The double quotes mean it is a string. You can do something like:
echo "Hello everybody"
either at the command line or the shell. Sometimes when people put stuff in quotes. you are supposed to replace what is in quotes with your own variable (removing the quotes), and sometimes people put quotes around the whole command you are supposed to type to show the what exactly you should type. For your example of "make -p" just type it without the quotes and it should work in both the command line and as a script.

How to use gitbash with file names that contain ASCII exclamation point characters like !0-MyFolder

I need to use gitbash on MS-Windows with file folder names that contain an exclamation point as the first characters, like "!0-MyProjectFolder" (without the quotation marks. I use the exclamation point to sort Microsoft Windows files to the top, since Windows does not provide a way to index and force sort order of files and folders.
Gitbash keeps giving me error messages:
I've tried several syntaxes already:
$ cd "!0-Projects-WIP"
bash: !0: event not found
$ cd "\!0-Projects-WIP"
bash: \!0: event not found
$ cd !0-Projects-WIP
bash: !0: event not found
Be clear that I am NOT parsing a string like '/New.*desktop.*is/!d' in the StackOverflow posting at How to address error "bash: !d': event not found" in Bash command substitution
VNCServerAndDisplayNumber="$(echo "${VNCServerResponse}" \
| sed '/New.*desktop.*is/!d' | awk -F" desktop is " '{print $2}')"
I am passing the directory name !0-Projects-WIP to GitBash, so that I can change into the directory named !0-Projects-WIP. I am not intending to do double-quoting or history expansion. If the exclamation point in the folder name appears to be a history expansion directive, then that is not the intended result. The ! must be escaped in my case so that it is read correctly as part of the folder name, and the shell command "cd" interprets it as a string.
I realize now that !0-Projects-WIP is probably a bad name to a Unix shell parser because it directs the command line parser to do something that was not my intention, but for the MS-Windows command line shell there is no confusion.
I got it to work with just a single escape character in front of the string:
$ cd \!0-Projects-WIP
rlysa#domainname MINGW64 /c/users/rlysak01/desktop/!0-Projects-WIP (master)
$
Simple solution. No quotation marks needed on the folder name string.

How to create one output file for each file passed to a loop in bash?

I have a file that I pass to a bash command that will create an output in a loop like so:
for file in /file/list/*
do
command
done
I wish to save the output that would have gone to standard out of each loop to a text file in my working directory. Currently I am trying this:
for file in /file/list/*
do
command | tee "$file_command output.txt"
done
What I expect to see are new files created in my current directory titled file1.txt_commandoutput.txt, file2.txt_commandoutput.txt, etc. The output of the command should be saved as a different file for each file. However I get only one file created and it's called ".txt" and can't be opened by any standard software on Mac. I am new to bash scripting, so help would be much appreciated!
Thanks.
Your problem comes from the variable name you're using:
"$file_command_output.txt" looks for a variable named file_command_output (the dot cannot be in the variable name, but the alphanumerical characters and the underscore all can).
What you're looking for is "${file}_command_output.txt" to make the variable name more explicit.
You have two issues in your script.
First, the wrong parameter/variable is expanded (file_command instead of file) because it's followed by a character that can be interpreted as part of the name (the underscore, _). To fix it, enclose the parameter name in braces, like this: ${file}_command (see Shell Parameter Expansion in bash manual).
Second, even with fixed variable name expansion, the file won't be created in your working directory, because the file holds an absolute pathname (/file/list/name). To fix it, you'll have to strip the directory from the pathname. You can do that with either basename command, or even better with a modified shell parameter expansion that will strip the longest matching prefix, like this: ${file##*/} (again, see Shell Parameter Expansion, section on ${parameter##word}).
All put together, your script now looks like:
#!/bin/bash
for file in /file/list/*
do
command | tee "${file##*/}_command output.txt"
done
Also, to just save the command output to a file, without printing it in terminal, you can use a simple redirection, instead of tee, like this: command > "${file##*/}_com...".
If you are not aware of xargs, try this:
$ ls
file
$ cat > file
one
two
three
$ while read this; do touch $this; done < ./file
$ ls
file one three two

Understanding script language

I'm a newbie to scripting languages trying to learn bash programming.
I have very basic question. Suppose I want to create three folders like $HOME/folder/
with two child folders folder1 and folder2.
If I execute command in shell like
mkdir -p $HOME/folder/{folder1,folder2}
folder will be created along with child folder.
If the same thing is executed through script I'm not able get expected result. If sample.sh contains
#!/bin/sh
mkdir -p $HOME/folder/{folder1,folder2}
and I execute sh ./sample.sh, the first folder will be created then in that a single {folder1,folder2} directory is created. The separate child folders are not created.
My query is
How the script file works when we compared to as terminal command? i.e., why is it not the same?
How to make it work?
bash behaves differently when invoked as sh, to more closely mimic the POSIX standard. One of the things that changes is that brace expansion (which is absent from POSIX) is no longer recognized. You have several options:
Run your script using bash ./sample.sh. This ignores the hashbang and explicitly uses bash to run the script.
Change the hashbang to read #!/bin/bash, which allows you to run the script by itself (assuming you set its execute bit with chmod +x sample.sh).
Note that running it as sh ./sample.sh would still fail, since the hashbang is only used when running the file itself as the executable.
Don't use brace expansion in your script. You could still use as a longer method for avoiding duplicate code:
for d in folder1 folder2; do
mkdir -p "$HOME/folder/$d"
done
Brace expansion doesn't happen in sh.
In sh:
$ echo {1,2}
produces
{1,2}
In bash:
$ echo {1,2}
produces
1 2
Execute your script using bash instead of using sh and you should see expected results.
This is probably happening because while your tags indicate you think you are using Bash, you may not be. This is because of the very first line:
#/bin/sh
That says "use the system default shell." That may not be bash. Try this instead:
#!/usr/bin/env bash
Oh, and note that you were missing the ! after #. I'm not sure if that's just a copy-paste error here, but you need the !.

Compressing using tar with bash script : Line n is a directory

I'm new with bash script and now trying to make a back-up file using it. What I'm facing is below script (line 47 in my bash file),
$(tar -czvPf ${folder_backup}/rajal/backup.tgz -C /var/www/tmk/app .)
always gimme error on the shell ./test: line 47: ./: Is a directory, while it works on Terminal, nicely.
FYI, folder app is folder with files and subfolders.
Could someone help me out? Thank you for any help.
$(command)
means: execute command and return its output. To illustrate, it is very often used like this:
result=$(foo 4 123)
After this is evaluated, result will hold whatever the command foo 4 123 output.
If you use that construct directly - not as an argument to another command, or in a variable assignment, the shell will try to execute the output of the command. While this is sometimes wanted, it often is not, and that's what you're seeing.
So just remove the $( ) from your command and run tar directly. If you want to capture the output of tar, either redirect it to a file or use the construct above.
Do note that $(command) and ${env_var_name} are completely different. Syntax matters.
Like #Mat Why do you have $( ) around the tar?, so i just removed it and it is working now :D

Resources