Execution on ./ not sh - bash

this is part of my current code
#! /bin/bash
#Take no arguments in
#checks to see if home/deleted is in existence
#creates the directory or file if it is missing
**(line 15)** function checkbin(){
if [ ! -c "~/deleted" ]; then
mkdir -p ~/deleted
fi
if [ ! -f "~/.restore.info" ]; then
touch ~/deleted/.restore.info
fi
}
I can call this code properly using ./remove [ARGS]
however when I call using sh remove [ARGS]
I receive the following error remove: 15: remove: Syntax error: "(" unexpected
ls -l on the file -rwxr-x--x
Does unix support execution on both sh and ./ ?

when executing with ./ /bin/bash is used (as define in shebang) whereas sh may be another interpreter or a link to bash which can have a different behaviour depending on how it is called sh.
bash is derived from sh but has some specific syntax:
for example in sh Function Definition Command is
fname() compound-command[io-redirect ...]
without function keyword.
For more details
Bash
Posix shell

If you want your script to run in sh as well as in bash, you need to be writing to the POSIX shell standard.
In this case, that means not using the Bash function keyword:
checkbin(){
test -c "~/deleted" || mkdir -p ~/deleted
test -f "~/.restore.info" || touch ~/deleted/.restore.info
}
If you're writing portable shell, then it's a good idea to use #!/bin/sh as your shebang.
(BTW, I assume you're aware that "~/deleted" and ~/deleted are in no way alike?)

Related

How to run a function when the cd command is run

I am kind of new to Linux and am just learning to make the computer do the work that I want.
So my wish is that whenever I use the cd
command, I want it to change directory and then list all the files present in them. If there are no arguments passed, I want the pwd command to run.
This is what I have done so far.
function cd {
if [ $# -eq 0 ]
then
pwd
else
cd "$1"; ls -l
fi
}
When I run this, it works fine when there are no arguments passed and it runs the pwd command. However, when I pass an argument, it does not display anything and it closes the terminal, which is'nt what I want.
When I changed the function name to ca though, and ran ca, it worked as expected.
Why is this so? Are there a list of aliases I am not allowed to use? How can I make it work?
If it exists, chpwd_functions is an array of function names, each of which will be called, in order, whenever the working directory changes. In your case, it could be used as follows:
foo () {
if [[ $PWD == $HOME ]]; then
pwd
else
ls -l
fi
}
chpwd_functions+=(foo)
You recursively call your function instead of calling the cd builtin.
In ZSH the builtin command can be used to execute a builtin explicitly suppressing shell function lookup. This is exactly what you need to implement a function that has the same name as a shell builtin
function cd {
if [ $# -eq 0 ]
then
pwd
else
builtin cd "$1" ; ls -l
fi
}
This applies to BASH as well.
Regarding the command builtin
In BASH you could use the command builtin to execute an external command or a builtin. That is where BASH is different from ZSH as in ZSH command executes external commands only.
Only in BASH
command cd works the same as builtin cd (assuming /bin/cd does not exist)
In ZSH
command cd would probably fail with cd: command not found unless /bin/cd exists
The "cd" command in Bash can take options, so using just $1 will drop the directory name. This passes everything:
function cd {
if [ $# -eq 0 ]
then
pwd
else
cd "$#"; ls -l
fi
}
I was having a similar issue having switched from bash to zsh. Example function...
go() {
if [[ $# == "code" ]]; then command cd "$CODE";
}
The solution for me was to 1. prefix my function with function and 2. replace command with builtin
function go() {
if [[ $# == "code" ]]; then builtin cd "$CODE";
}

How to translate an alias into a real file?

Most of the time, an alias works well, but some times, the command is executed by other programs, and they find it in the PATH, in this situation an alias not works as well as a real file.
e.g.
I have the following alias:
alias ghc='stack exec -- ghc'
And I want to translate it into an executable file, so that the programs which depending on it will find it correctly. And the file will works just like the alias does, including how it process it's arguments.
So, is there any tool or scripts can help doing this?
Here is my solution, I created a file named ghc as following:
#!/bin/sh
stack exec -- ghc "$#"
The reason why there is double quote around $# is explained here: Propagate all arguments in a bash shell script
So, is there any tool or scripts can help doing this?
A lazy question for a simple problem... Here's a function:
alias2script() {
if type "$1" | grep -q '^'"$1"' is aliased to ' ; then
alias |
{ sed -n "s#.* ${1}='\(.*\)'\$##\!/bin/sh\n\1 \"\${\#}\"#p" \
> "$1".sh
chmod +x "$1".sh
echo "Alias '$1' hereby scriptified. To run type: './$1.sh'" ;}
fi; }
Let's try it on the common bash alias ll:
alias2script ll
Output:
Alias 'll' hereby scriptified. To run type: './ll.sh'
What's inside ll.sh:
cat ll.sh
Output:
#!/bin/sh
ls -alF "${#}"

bash execute commands as different user with su

I have the most simple issue and I am so not certain what I am doing wrong.
I have a simple shell script using /bin/sh
inside the script I have the following:
exec_as_wwwdata() {
if [ $(whoami) = ${WWW_USER} ]]; then
$#
else
su -s /bin/sh -c '$#' ${WWW_USER}
fi
}
I am calling it with
exec_as_wwwdata composer config -g github-oauth.github.com $COMPOSER_GITHUB_TOKEN
it just does nothing, no error message nothing.
If I call the following directly inside the script
su -s /bin/sh -c 'composer config -g github-oauth.github.com $COMPOSER_GITHUB_TOKEN' ${WWW_USER}
it works.
What am I doing wrong here?
Based on feedback I have changed it to this
exec_as_wwwdata() {
if [ $(whoami) = ${WWW_USER} ]]; then
$#
else
su -s /bin/sh -c '"$#"' "$WWW_USER" _ "$#"
fi
}
Although when I am calling it with the following arguments
exec_as_wwwdata /usr/local/bin/php /usr/local/bin/composer create-project --repository-url=xxxx .
I receive the following error message
su: unrecognized option '--repository-url=
I think there is issue with -- in the string. How can I escape that ?
There are two overlapping uses of $# here, and you've inadvertently stumbled on a partial correct solution. -c expects a single word, while "$#" would produce multiple distinct words. The correct solution would be
su -s /bin/sh -c '"$#"' "$WWW_USER" _ "$#"
The short version: you don't want to build a command string from the current parameters; you want to pass them as arguments to a hard-coded command string to let the new shell expand things appropriately.
A breakdown:
-s /bin/sh - use /bin/sh instead of the appropriate users's login shell
-c '"$#"' run the command "$#", as desired. Note this is a hard-coded value; the new shell will expand its positional parameters correctly once it has started.
"$WWW_USER" - specify the user to run the shell as
_ - specify the value of $0 in the shell being run. You probably don't care what this value is; you just need some placeholder to prevent your first real argument from being treated as the value for $0.
"$#" pass the current positional parameters as arguments to the new shell, which will expand its "$#" to these values.

How do I change Korn(ksh) version dynamically based on platform?

I wanted to use the /usr/bin/ksh93 interpreter on AIX and Linux wherever possible but switch to /usr/bin/ksh where it's not applicable like Mac OS X and wanted the script to be universally compatible in unix. I don't think there is any fallback mechanism in shebang
Since ksh and sh have some syntax in common, you can prefix the start of the
script with a test for ksh or ksh93 in the PATH and rerun the script with
the right interpreter. Replace the #! with the pathname to sh. (Hopefully
it is the same on both machines, or you are back where you started. You can
still try #!/usr/bin/env sh if your env will find the path for you). Add:
#!/bin/sh
if [ "$DONEIT" != true ]
then export DONEIT=true # avoid recursion
if command -v ksh > /dev/null 2>&1
then exec ksh $0 "$#"
else exec ksh93 $0 "$#"
fi
fi
... rest of your script ...
Note: command -v is the POSIX way for finding a command's path.
(Often in these situations, at the installation of a package a script goes
through the #! files and updates the interpreter path to that needed by the
target machine).
Alternatively, you could replace the #! line by any fixed path you control, eg #!/home/user/myksh, and link that file to the right ksh.
You can make a symbolic links.
if [ -f /usr/bin/ksh93 ]; then
ln -s /usr/bin/ksh93 /usr/bin/localksh
else
ln -s /usr/bin/ksh /usr/bin/localksh
fi
The shebang will be #!/usr/bin/localksh.
I would prefer using a normal shebang #!/bin/ksh, but when that one already exists and is the wrong version you will be stuck.

Sourcing a script file in bash before starting an executable

I'm trying to write a bash script that "wraps" whatever the user wants to invoke (and its parameters) sourcing a fixed file just before actually invoking it.
To clarify: I have a "ConfigureMyEnvironment.bash" script that must be sourced before starting certain executables, so I'd like to have a "LaunchInMyEnvironment.bash" script that you can use as in:
LaunchInMyEnvironment <whatever_executable_i_want_to_wrap> arg0 arg1 arg2
I tried the following LaunchInMyEnvironment.bash:
#!/usr/bin/bash
launchee="$#"
if [ -e ConfigureMyEnvironment.bash ];
then source ConfigureMyEnvironment.bash;
fi
exec "$launchee"
where I have to use the "launchee" variable to save the $# var because after executing source, $# becomes empty.
Anyway, this doesn't work and fails as follows:
myhost $ LaunchInMyEnvironment my_executable -h
myhost $ /home/me/LaunchInMyEnvironment.bash: line 7: /home/bin/my_executable -h: No such file or directory
myhost $ /home/me/LaunchInMyEnvironment.bash: line 7: exec: /home/bin/my_executable -h: cannot execute: No such file or directory
That is, it seems like the "-h" parameter is being seen as part of the executable filename and not as a parameter... But it doesn't really make sense to me.
I tried also to use $* instead of $#, but with no better outcoume.
What I'm doing wrong?
Andrea.
Have you tried to remove double quotes in exec command?
Try this:
#!/usr/bin/bash
typeset -a launchee
launchee=("$#")
if [ -e ConfigureMyEnvironment.bash ];
then source ConfigureMyEnvironment.bash;
fi
exec "${launchee[#]}"
That will use arrays for storing arguments, so it will handle even calls like "space delimited string" and "string with ; inside"
Upd: simple example
test_array() { abc=("$#"); for x in "${abc[#]}"; do echo ">>$x<<"; done; }
test_array "abc def" ghi
should give
>>abc def<<
>>ghi<<
You might want to try this (untested):
#!/usr/bin/bash
launchee="$1"
shift
if [ -e ConfigureMyEnvironment.bash ];
then source ConfigureMyEnvironment.bash;
fi
exec "$launchee" $#
The syntax for exec is exec command [arguments], however becuase you've quoted $launchee, this is treated as a single argument - i.e., the command, rather than a command and it's arguments. Another variation may be to simply do: exec $#
Just execute it normally without exec
#!/usr/bin/bash
launchee="$#"
if [ -e ConfigureMyEnvironment.bash ];
then source ConfigureMyEnvironment.bash;
fi
$launchee
Try dividing your list of argumets:
ALL_ARG="${#}"
Executable="${1}"
Rest_of_Args=${ALL_ARG##$Executable}
And try then:
$Executable $Rest_of_Args
(or exec $Executable $Rest_of_Args)
Debugger

Resources