BASH - xargs command not found - linux

I am trying to create a BASH script that will run a command for me. This is an example of one of the commands:
function systemStart {
./ORBMarkerDetection $1 $2 $3 | xargs -n3 java -jar ../../system/layers/out/artifacts/layers_jar/layers.jar
}
But when this is ran I am getting the error (referring to the above line):
./runActivities.sh: line 7: xargs: command not found
I am able to run this command in the terminal with success so I am not sure why this will not run within a BASH script?
I am calling the function like so:
systemStart $PATH/1.1/cupCupboard.png $PATH/1.1/kitchenDoor.png $PATH/1.1/tap.png

You are apparently using the variable name PATH for your own purposes, but you can't do that -- PATH is a reserved variable, and changing it will cause the shell to not find commands (not just xargs but basically any command).
In general, you should avoid using uppercase variable names; then you can be sure yours will never conflict with a built-in shell variable.

(You may need to put the fully-qualified path in your script?)
The command which can tell you the fully-qualified path for things.
robert#debian:~$ which xargs
/usr/bin/xargs
locate can also tell you the location of files
Lastly, then a brute-force full filesystem search using find:
robert#debian:~$ find / -name "xargs" 2> /dev/null
/usr/bin/xargs

Related

Linux command line, reverse polish notation

ls /tmp
How can I run the same command but using reverse polish notation?
Is there a mode that would allow me to do this or something similar to that?
I could use xargs but that's a lot more typing:
echo /tmp | xargs ls
This would be ideal:
/tmp ls
or
/tmp | ls
Bash (I assume you are using it) is a shell for unixoid systems.
As far as I know, bash doesn't provide such a mode. You could use a different shell that provides this feature. Searching in the web, this was my first result: https://github.com/iconmaster5326/RPOS, but maybe it is far from stable ;)
Alternatively, you can make a command that reverses it's argument list and execute it.
The usage would be like this:
reversex /tmp ls
reversex A.txt B.txt cp
Here is an example of such a command:
#!/bin/bash
for i in "$#"
do
CMDLINE="$i $CMDLINE"
done
$CMDLINE
If you name it /usr/local/bin/reversex and make it executable, you should be able to use simple reverse commands with the prefix reversex. I can not give a warranty that it works. Note that the arguments are parsed twice and have to be escaped twice, too.

Bash printf %q invalid directive

I want to change my PS1 in my .bashrc file.
I've found a script using printf with %q directive to escape characters :
#!/bin/bash
STR=$(printf "%q" "PS1=\u#\h:\w\$ ")
sed -i '/PS1/c\'"$STR" ~/.bashrc
The problem is that I get this error :
script.sh: 2: printf: %q: invalid directive
Any idea ? Maybe an other way to escape the characters ?
The printf command is built into bash. It's also an external command, typically installed in /usr/bin/printf. On most Linux systems, /usr/bin/printf is the GNU coreutils implementation.
Older releases of the GNU coreutils printf command do not support the %q format specifier; it was introduced in version 8.25, released 2016-10-20. bash's built-in printf command does -- and has as long as bash has had a built-in printf command.
The error message implies that you're running script.sh using something other than bash.
Since the #!/bin/bash line appears to be correct, you're probably doing one of the following:
sh script.sh
. script.sh
source script.sh
Instead, just execute it directly (after making sure it has execute permission, using chmod +x if needed):
./script.sh
Or you could just edit your .bashrc file manually. The script, if executed correctly, will add this line to your .bashrc:
PS1=\\u#\\h:\\w\$\
(The space at the end of that line is significant.) Or you can do it more simply like this:
PS1='\u#\h:\w\$ '
One problem with the script is that it will replace every line that mentions PS1. If you just set it once and otherwise don't refer to it, that's fine, but if you have something like:
if [ ... ] ; then
PS1=this
else
PS1=that
fi
then the script will thoroughly mess that up. It's just a bit too clever.
Keith Thompson has given good advice in his answer. But FWIW, you can force bash to use a builtin command by preceding the command name with builtin eg
builtin printf "%q" "PS1=\u#\h:\w\$ "
Conversely,
command printf "%s\n" some stuff
forces bash to use the external command (if it can find one).
command can be used to invoke commands on disk when a function with the same name exists. However, command does not invoke a command on disk in lieu of a Bash built-in with the same name, it only works to suppress invocation of a shell function. (Thanks to Rockallite for bringing this error to my attention).
It's possible to enable or disable specific bash builtins (maybe your .bashrc is doing that to printf). See help enable for details. And I guess I should mention that you can use
type printf
to find out what kind of entity (shell function, builtin, or external command) bash will run when you give it a naked printf. You can get a list of all commands with a given name by passing type the -a option, eg
type -a printf
You can use grep to see the lines in your .bashrc file that contain PS1:
grep 'PS1' ~/.bashrc
or
grep -n0 --color=auto 'PS1=' ~/.bashrc
which gives you line numbers and fancy coloured output. And then you can use the line number to force sed to just modify the line you want changed.
Eg, if grep tells you that the line you want to change is line 7, you can do
sed -i '7c\'"$STR" ~/.bashrc
to edit it. Or even better,
sed -i~ '7c\'"$STR" ~/.bashrc
which backs up the original version of the file in case you make a mistake.
When using sed -i I generally do a test run first without the -i so that the output goes to the shell, to let me see what the modifications do before I write them to the file.

linux find exec positional parameter

I'm currently using the following line to find a number of scripts and execute them. The scripts are named as : my_script_0.sh, my_script_1.sh, etc.
find $MY_HOME/SHELL/my_script_[0-9].sh -type f -exec csh -c '"$1" >& "$logfile" &' {} \;
This works fine except that now I would like to create a unique $logfile for each of the executed scripts.
I realize that using awk I could do something like this to grab the number of the file and then potentially use that in the logfile name.
find $MY_HOME/SHELL/my_script_[0-9].sh -type f | awk -F"\\.|_" '{print $4}'
The issue is that I don't believe I can use awk with the original statement since I need the positional parameter to be the full path/filename.
Ideally I would like to simply use another positional parameter with the find -exec command. Does it exist?
The -exec option to find simply excutes the given arguments. Since you pass it csh -c ... it will start a new shell to which you can optionally pass some arguments (one being {}). These arguments are then set as positional arguments $1, $2, $3, ... ,$n in the new shell. In this case the find results are passed one by one and used as $1 in the shell.
I'll suggest an alternative to your find command which uses a loop instead:
foreach script ( $MY_HOME/SHELL/my_script_[0-9].sh )
csh -c "$script >& $script:r.log"
end
The :r strips the extension of the variable it's attached to so we get the logfile: my_script_n.log for the script my_script_n.sh.
I checked out these references on C-shell syntax:
CSH Scripting Basics
IBM

UNIX, change the interpreter line in all shell scripts

Can someone please tell me how to find out the pathname of the KornShell (ksh) on my machine and then change the interpreter line in all shell scripts (.sh) in the current directory that show a different pathname for ksh?
This will give you the path to Korn shell:
which ksh
And this will replace shebang in all shell scripts:
sed -i 's/#!\/bin\/bash/#!insert escaped ksh path here/' *.sh
To find out where ksh lives:
whence -a ksh
To change all shell scripts in the current directory to use ksh:
sed -i '1{/^#!\/[^ ]*\/\(ba\|\)sh\( *\)/s||#!/bin/ksh\2|}' *.sh
This will match for sh or bash and preserve any spaces (and arguments) that appear afterwards. It only acts on the first line so it won't touch similar lines that may appear later in the file.
Substitute the actual location of your ksh executable or use /usr/bin/env ksh.
Use sed -i .bak if you want to backup the changed files.
Put this in the first line of each file (shebang line):
#!/usr/bin/env ksh
If you need to replace you can use sed/awk:
find -name '*.sh' | xargs perl -pi -e "s{^#!/usr/bin/sh}{#!/usr/bin/env ksh}"
This command will locate the korn shell executable in your UNIX environment:
which ksh
If no output is returned, then the korn shell is missing. (At least not found in your environment PATH.) You can get it from the KornShell site or install it via your software package manager system.
In order to replace the interpreter line in all shell scripts (*.sh), you could run something like this:
sed -i "s/^#\!.*$/#\!\/bin\/ksh/" *.sh
The -i option is to edit files in place. (Warning: Test this command without the -i first, to avoid file modifications.)
The quoted argument is the pattern to match all lines starting with "#!" and replace them with "#!/bin/ksh". (Note that some special characters need to be escaped with "\" before.) You may need to customize this argument if it isn't exactly the replacement you're looking for.
On my machine only this works
sed -i "s/^#\!.*/#\!\/usr\/bin\/perl/" *.sh
Everyother throws an error.

bash: get list of commands starting with a given string

Is it possible to get, using Bash, a list of commands starting with a certain string?
I would like to get what is printed hitting <tab> twice after typing the start of the command and, for example, store it inside a variable.
You should be able to use the compgen command, like so:
compgen -A builtin [YOUR STRING HERE]
For example, "compgen -A builtin l" returns
let
local
logout
You can use other keywords in place of "builtin" to get other types of completion. Builtin gives you shell builtin commands. "File" gives you local filenames, etc.
Here's a list of actions (from the BASH man page for complete which uses compgen):
alias Alias names. May also be specified as -a.
arrayvar Array variable names.
binding Readline key binding names.
builtin Names of shell builtin commands. May also be specified as -b.
command Command names. May also be specified as -c.
directory Directory names. May also be specified as -d.
disabled Names of disabled shell builtins.
enabled Names of enabled shell builtins.
export Names of exported shell variables. May also be specified as -e.
file File names. May also be specified as -f.
function Names of shell functions.
group Group names. May also be specified as -g.
helptopic Help topics as accepted by the help builtin.
hostname Hostnames, as taken from the file specified by the HOSTFILE shell
variable.
job Job names, if job control is active. May also be specified as
-j.
keyword Shell reserved words. May also be specified as -k.
running Names of running jobs, if job control is active.
service Service names. May also be specified as -s.
setopt Valid arguments for the -o option to the set builtin.
shopt Shell option names as accepted by the shopt builtin.
signal Signal names.
stopped Names of stopped jobs, if job control is active.
user User names. May also be specified as -u.
variable Names of all shell variables. May also be specified as -v.
A fun way to do this is to hit M-* (Meta is usually left Alt).
As an example, type this:
$ lo
Then hit M-*:
$ loadkeys loadunimap local locale localedef locale-gen locate
lockfile-create lockfile-remove lockfile-touch logd logger login
logname logout logprof logrotate logsave look lorder losetup
You can read more about this in man 3 readline; it's a feature of the readline library.
If you want exactly how bash would complete
COMPLETIONS=$(compgen -c "$WORD")
compgen completes using the same rules bash uses when tabbing.
JacobM's answer is great. For doing it manually, i would use something like this:
echo $PATH | tr : '\n' |
while read p; do
for i in $p/mod*; do
[[ -x "$i" && -f "$i" ]] && echo $i
done
done
The test before the output makes sure only executable, regular files are shown. The above shows all commands starting with mod.
Interesting, I didn't know about compgen. Here a script I've used to do it, which doesn't check for non-executables:
#!/bin/bash
echo $PATH | tr ':' '\0' | xargs -0 ls | grep "$#" | sort
Save that script somewhere in your $PATH (I named it findcmd), chmod u+w it, and then use it just like grep, passing your favorite options and pattern:
findcmd ^foo # finds all commands beginning with foo
findcmd -i -E 'ba+r' # finds all commands matching the pattern 'ba+r', case insensitively
Just for fun, another manual variant:
find -L $(echo $PATH | tr ":" " ") -name 'pattern' -type f -perm -001 -print
where pattern specifies the file name pattern you want to use. This will miss commands that are not globally executable, but which you have permission for.
[tested on Mac OS X]
Use the -or and -and flags to build a more comprehensive version of this command:
find -L $(echo $PATH | tr ":" " ") -name 'pattern' -type f
\( \
-perm -001 -or \
\( -perm -100 -and -user $(whoami)\) \
\) -print
will pick up files you have permission for by virtue of owning them. I don't see a general way to get all those you can execute by virtue of group affiliation without a lot more coding.
Iterate over the $PATH variable and do ls beginningofword* for each directory in the path?
To get it exactly equivalent, you would need to filter out only executable files and sort by name (should be pretty easy with ls flags and the sort command).
What is listed when you hit are the binary files in your PATH that start with that string. So, if your PATH variable contains:
PATH=/usr/local/bin:/usr/bin:/bin:/usr/games:/usr/lib/java/bin:/usr/lib/java/jre/bin:/usr/lib/qt/bin:/usr/share/texmf/bin:.
Bash will look in each of those directories to show you the suggestions once you hit . Thus, to get the list of commands starting with "ls" into a variable you could do:
MYVAR=$(ls /usr/local/bin/ls* /usr/bin/ls* /bin/ls*)
Naturally you could add all the other directories I haven't.

Resources