Can't execute git command from bash [duplicate] - bash

I'm trying to write a little script to list a directory from a given variable.
However, I can't run ls at all after reading my input into the variable PATH.
#!/system/bin/sh
echo "enter directory for listing"
read "PATH"
ls "$PATH" -R > list.txt
This exits with:
ls: not found
...and writes nothing to list.txt.

The variable name PATH is already reserved for a different purpose: It lists all the possible locations searched to find commands not built into the shell.
ls is such a command. Thus, when you change the value of PATH, you change the way the shell tries to look for the ls executable; unless the new value of PATH includes a directory with a ls executable in it, any further attempts to run ls (or other commands not built into the shell) will fail.
Instead, use a different variable name -- ideally, including at least one lower-case character, to avoid conflict with (all-uppercase) builtins and environment variables.
Thus, one corrected form might be:
#!/system/bin/sh
echo "enter directory for listing"
IFS= read -r path
ls -R -- "$path" > list.txt
Note that the -R is moved before the "$path" in this case -- while GNU systems will allow optional arguments to be after positional arguments, many older UNIX systems will only treat flags (like -R) as valid if they're found before the first non-flag/option argument.

I fixed it by resetting my iTerm 2.

Related

The `ls` command is interpreting my directory with spaces as multiple directories [duplicate]

This question already has answers here:
Why does shell ignore quoting characters in arguments passed to it through variables? [duplicate]
(3 answers)
Closed 3 years ago.
I'm trying to pass a dynamic command that executes ls as a string that lists the files of a directory that contains spaces. However, my ls command always interprets my one directory containing spaces as multiple directories no matter what I do.
Consider the following simplified version of my shell script:
#!/bin/sh
export WORK_DIR="/Users/jthoms/Dropbox (My Company)/backup-jthoms/Work"
echo "WORK_DIR=$WORK_DIR"
export LS_CMD="ls -A \"$WORK_DIR/dependencies/apache-tomcat-8.0.45/logs\""
echo "LS_CMD=$LS_CMD"
if [ -n "$($LS_CMD)" ]
then
echo "### Removing all logs"
sudo rm "$WORK_DIR/dependencies/apache-tomcat-8.0.45/logs/*"
else
echo "### Not removing all logs"
fi
This script results in the following output:
WORK_DIR=/Users/jthoms/Dropbox (My Company)/backup-jthoms/Work
LS_CMD=ls -A "/Users/jthoms/Dropbox (My Company)/backup-jthoms/Work/dependencies/apache-tomcat-8.0.45/logs"
ls: "/Users/jthoms/Dropbox: No such file or directory
ls: (My: No such file or directory
ls: Company)/backup-jthoms/Work/dependencies/apache-tomcat-8.0.45/logs": No such file or directory
### Not removing all logs
How can I correctly escape my shell variables so that the ls command interprets my directory as a single directory containing spaces instead of multiple directories?
I recently changed this script which used to work fine for directories containing no spaces but now doesn't work for this new case. I'm working on Bash on MacOSX. I have tried various forms of escaping, various Google searches and searching for similar questions here on SO but to no avail. Please help.
Variables are for data. Functions are for code.
# There's no apparent need to export this shell variable.
WORK_DIR="/Users/jthoms/Dropbox (My Company)/backup-jthoms/Work"
echo "WORK_DIR=$WORK_DIR"
ls_cmd () {
ls -A "$1"/dependencies/apache-tomcat-8.0.45/logs
}
if [ -n "$(ls_cmd "$WORK_DIR")" ]; then
then
echo "### Removing all logs"
sudo rm "$WORK_DIR/dependencies/apache-tomcat-8.0.45/logs/"*
else
echo "### Not removing all logs"
fi
However, you don't need ls for this at all (and in general, you should avoid parsing the output of ls). For example,
find "$WORK_DIR/dependencies/apache-tomcat-8.0.45/logs/" -type f -exec rm -rf {} +
You could use
# ...
if [ -n "$(eval "$LS_CMD")" ]
# ...
See http://mywiki.wooledge.org/BashFAQ/050
Or even
# ...
if [ -n "$(bash -c "$LS_CMD")" ]
# ...
But you are probably better off using a dedicated function and/or even something more specific to your problem (using find instead of ls is usually a good idea in these cases, see some examples in the answers for this question).
Use arrays, not strings, to store commands:
ls_cmd=(ls -A "$WORK_DIR/dependencies/apache-tomcat-8.0.45/logs")
echo "ls_cmd=${ls_cmd[*]}"
if [ -n "$("${ls_cmd[#]}")" ]; then …
(The syntax highlighting on the last line is incorrect and misleading: we’re not unquoting ${ls_cmd[#]}; in reality, we are using nested quotes via a subshell here.)
That way, word splitting won’t interfere with your command.
Note that you can’t export arrays. But you don’t need to, in your code.
As others have noted, it’s often a better idea to use functions here. More importantly, you shouldn’t parse the output of ls. There are always better alternatives.

bash: for loop to retrieve all files in directory only returns one

I have made a directory with lots of files with:
samplefile_111222015_reporting_{1..13}
I am trying to create a vi script where when I enter the directory as an argument to the command e.g.
sh myScript $HOME/theDir/*
then it copies all the files in that directory to a new one I made. Although right now, I'm having problems with the for loop alone.
This is what I have in my script:
for f in $1;
do
echo "$f"
done
but when i enter sh myScript $HOME/theDir, I get back the name of the first file only (samplefile_111222015_reporting_1). why the first file? Is this not a for loop>
# Because of the wild card expansion, all the files in the directory are
# already made available to the script through arguments
# So do the following to get all the file listing
for f ; do echo $f; done
This is because each file is passed as a separate argument and you're only looping over $1, which is the first argument.
Instead, you most likely want to loop over "$#", which is every argument starting from $1.
The man page for bash, under the Special Parameters section, details the special parameters available in more detail.

how to find last selected character in shell script

I have assigned following string to a variable.
line="/remotepath/mypath/localpath/common/location.txt"
If I want to access common location (/remotepath/mypath/localpath/common)
how can I split this in last "/" ?
In most unix-style operating systems, there's a program called dirname which does this for you:
$ line="/remotepath/mypath/localpath/common/location.txt"
$ dirname "$line"
/remotepath/mypath/localpath/common
The command is of course available from any shell, since it's not part of the shell per-se, though you might need to assign the variable differently. For example, in csh/tcsh:
% setenv line "/remotepath/mypath/localpath/common/location.txt"
% dirname "$line"
/remotepath/mypath/localpath/common
If you want to strip off the file using shell commands alone, you'll need to specify what shell you're using, since commands vary. For example, in /bin/sh or similar shells (like bash), you could use "Parameter expansion" (look it up in the man page, there's lots of good stuff):
$ line="/remotepath/mypath/localpath/common/location.txt"
$ echo "${line%/*}
/remotepath/mypath/localpath/common
Hey you can use below command if your line variable contains same number of directories always
echo $line | cut -d "/" -f1-5
line="/remotepath/mypath/localpath/common/location.txt"
path="${line%/*}"
file="${line##*/}"
## contents of the variables after extraction
# path is '/remotepath/mypath/localpath/common'
# file is 'location.txt'
It's called parameter expansion/substring extraction in bash.

"ls: not found" after running "read PATH"

I'm trying to write a little script to list a directory from a given variable.
However, I can't run ls at all after reading my input into the variable PATH.
#!/system/bin/sh
echo "enter directory for listing"
read "PATH"
ls "$PATH" -R > list.txt
This exits with:
ls: not found
...and writes nothing to list.txt.
The variable name PATH is already reserved for a different purpose: It lists all the possible locations searched to find commands not built into the shell.
ls is such a command. Thus, when you change the value of PATH, you change the way the shell tries to look for the ls executable; unless the new value of PATH includes a directory with a ls executable in it, any further attempts to run ls (or other commands not built into the shell) will fail.
Instead, use a different variable name -- ideally, including at least one lower-case character, to avoid conflict with (all-uppercase) builtins and environment variables.
Thus, one corrected form might be:
#!/system/bin/sh
echo "enter directory for listing"
IFS= read -r path
ls -R -- "$path" > list.txt
Note that the -R is moved before the "$path" in this case -- while GNU systems will allow optional arguments to be after positional arguments, many older UNIX systems will only treat flags (like -R) as valid if they're found before the first non-flag/option argument.
I fixed it by resetting my iTerm 2.

How to name output file according to a command line argument in a bash script?

These lines work when copy-pasted to the shell but don't work in a script:
ls -l file1 > /path/`echo !#:2`.txt
ls -l file2 > /path/`echo !#:2`.txt
 
ls -l file1 > /path/$(echo !#:2).txt
ls -l file2 > /path/$(echo !#:2).txt
What's the syntax for doing this in a bash script?
If possible, I would like to know how to do this for one file and for all files with the same extension in a folder.
Non-interactive shell has history expansion disabled.
Add the following two lines to your script to enable it:
set -o history
set -o histexpand
(UPDATE: I misunderstood the original question as referring to arguments to the script, not arguments to the current command within the script; this is a rewritten answer.)
As #choroba said, history is disabled by default in scripts, because it's not really the right way to do things like this in a script.
The preferred way to do things like this in a script is to store the item in question (in this case the filename) in a variable, then refer to it multiple times in the command:
fname=file1
ls -l "$fname" > "/path/$fname.txt"
Note that you should almost always put variable references inside double-quotes (as I did above) to avoid trouble if they contain spaces or other shell metacharacters. If you want to do this for multiple files, use a for loop:
for fname in *; do # this will repeat for each file (or directory) in the current directory
ls -l "$fname" > "/path/$fname.txt"
done
If you want to operate on files someplace other than the current directory, things are a little more complicated. You can use /inputpath/*, but it'll include the path along with each filename (e.g. it'd run the loop with "/inputpath/file1", "/inputpath/file2", etc), and if you use that directly in the output redirect you'll get something like > /path/inputpath/file1.txt (i.e. the two different paths will get appended together), probably not what you want. In this case, you can use the basename command to strip off the unwanted path for output purposes:
for fpath in /inputpath/*; do
ls -l "$fpath" > "/path/$(basename "$fpath").txt"
done
If you want a list of files with a particular extension, just use *.foo or /inputpath/*.foo as appropriate. However, in this case you'll wind up with the output going to files named e.g. "file1.foo.txt"; if you don't want stacked extensions, basename has an option to trim that as well:
for fpath in /inputpath/*.foo; do
ls -l "$fpath" > "/path/$(basename "$fpath" .foo).txt"
done
Finally, it might be neater (depending how complex the actual operation is, and whether it occurs multiple times in the script) to wrap this in a function, then use that:
doStuffWithFile() {
ls -l "$1" > "/path/$(basename "$1" "$2").txt"
}
for fpath in /inputpath/*.foo; do
doStuffWithFile "$fpath" ".foo"
done
doStuffWithFile /otherpath/otherfile.bar .bar

Resources