How do I translate this bash function to fish shell? - bash

function run () {
nohup python $1 > nohup.out &
}
On the command line I call this as "run scriptname.py" and bash executes the following command:
python scriptname.py > nohup.out &
Can you help me translate this to fish.
I have this so far..
function run
bash -c "nohup python $1 > nohup.out &"
end
When I call source on ~/.config/fish/config.fish
This exists simply saying
Error when reading file: ~/.config/fish/config.fish
without providing any helpful hints as to what the error is.

There's really no need to execute bash here, fish can also execute nohup, the redirections also work and such.
There's a minor difference in that, instead of $1 and $2 and so on, arguments to fish functions are stored in the $argv array.
function run
nohup python $argv > nohup.out &
end
This will expand $argv to all elements of that as one element each, so run script.py banana would run nohup python script.py banana > nohup.out &. If you truly want just one argument to be passed, you'd need $argv[1].
I actually have no idea why your definition should cause an error when sourcing config.fish - which fish version are you using?

This is a perfectly valid (and more correct) replacement for your function in fish:
function run
bash -c 'nohup python "$#" > nohup.out &' _ $argv
end
This is an equivalent to the native-bash function:
run() {
nohup python "$#" </dev/null >nohup.out 2>&1 &
}
...which, personally, I would suggest rewriting to use disown rather than nohup.
With respect to the error seen from fish, I'd suggest paying attention to any other (not syntax-related) which may have impacted whether your file could be read -- permissions, etc.

Related

Using [ nohup ksh ] to run a script cannot see the sleep process

I am using CentOS 7.6 and bash shell inside VirtualBox .
I came across a script named MotherScript in my site having this statement :
nohup ksh MyScript &
Both MotherScript and MyScript have a shebang line
#!/bin/ksh
After login, I just fire the MotherScript.
In my previous companies, usually, I used this statement :
nohup MyScript &
Is there any functional/strange/usage difference between these 2 statements ?
Inside MyScript, there is a sleep command to sleep 1 hour repeatedly in a while loop for 7 days.
However, very strange, I cannot see this sleep process using :
ps -ef | grep sleep
Therefore, I cannot kill the sleep process.
Does this nohup ksh play the trick ?
Anyone know the reason behind ?
Thanks.
Alvin
nohup ksh MyScript &
This will run the script using ksh(korn) shell.
nohup MyScript &
This will pick your default shell which you can check using below command
echo $SHELL
you can also change default shell using chsh Utility

command invoking new shell and script is broken

I have two commands:
/aka/ball/barry/pet elephant/rhino
which invoke new shell and I have command
cleartool setview "/view/epp/lpp/tpp.sh" $VIEW
which also invoke new shell. Is it possible to run both in a single script one by one?
Probably; although it depends. If these shells are designed to be interactive, you can still probably make them work by sending them commands to end the subshells, such as exit 0:
$ /aka/ball/barry/pet elephant/rhino <<< 'exit 0'
If that successfully exits the shell, you can just do it in serial:
#!/bin/bash
##
# Your script?
…stuff
/aka/ball/barry/pet elephant/rhino <<< 'exit 0'
cleartool setview "/view/epp/lpp/tpp.sh" "$VIEW" <<< 'exit 0'
…morestuff
Without knowing more about the nature of the shells these programs invoke, it's hard to say for sure.

On writing a Linux shell script to safely detach programs from a terminal

I'm trying to write a Linux shell script (preferably bash),
supposedly named detach.sh, to safely detach programs from a terminal,
such that:
Invocation: ./detach.sh prog [arg1 arg2 ...].
Is exec-able, eg. by running this in your shell:
exec ./detach.sh prog [arg1 arg2 ...]
With proper quoting (mainly handling of arguments containing whitespaces).
Discards the outputs (since they are unneeded).
Does not use screen, tmux, etc.
(same reason with 4, plus no need for an extra babysitting process).
Uses (reasonably) portable commands and programs,
and no things like start-stop-daemon which is quite distro-specific.
I have thought of several ways (shebang lines #!/bin/bash neglected
for the sake of briefness):
nohup:
nohup "$#" >& /dev/null &
disown:
"$#" >& /dev/null &
disown
setsid:
setsid "$#" >& /dev/null &
Using a subshell:
("$#" >& /dev/null &)
nohup/setsid combined with subshell:
# Or alternatively:
# (nohup "$#" >& /dev/null &)
(setsid "$#" >& /dev/null &)
When using gedit as the test program (substituting the "$#" part),
condition 1 can be satisfied with all the above methods,
but condition 2 can be satisfied with none.
However, if an arbitrary program (but not a shell builtin) is appended to script 5,
all the conditions seem to be satisfied (at least for me in the gedit case).
For example:
(setsid "$#" >& /dev/null &)
# Not just `true' because it is also a shell builtin.
/bin/true
Anyone with an idea about an explanation of the above phenomenons
and how to correctly implement the requirements?
EDIT:
With condition 2, I mean the program should be detached from the terminal but runs as usual otherwise. For example, with the gedit case, the condition fails if gedit just exits immediately right after the process of the script has ended.
Upon closer investigation, these previously unnoticed facts were revealed:
Both scripts 3 and 5 (the setsid variant only) will
satisfy all the conditions if a /bin/true is appended to the script.
These scripts, as modified in fact 1, will work as well if
/bin/true is replaced with for i in {0..9999}; do :; done.
Therefore we can conclude that:
(From fact 1)
Multiple levels of detaching (as in script 5) is unnecessary,
and the key is to use the right utility (setsid).
(From fact 2)
A suitable delay before bash exit is necessary for the success of the script.
(Calling external program /bin/true consumes some time,
just like the pure-bash time consumer for i in {0..9999}; do :; done.)
I have not looked at the source code, but I guess a possible explanation
is that bash may exit before setsid finishes configuring the execution
environment of the program to run, if an appropriate delay is not applied.
And finally, an optimal solution should be
#!/bin/bash
setsid "$#" >& /dev/null &
sleep 0.01
EDIT 1:
The necessity of a delay has been explained here. Many thanks to #wilx!
EDIT 2:
(Thanks to #MateiDavid) we seem to have forgotten to redirect the standard input, and a better way would be:
#!/bin/bash
setsid "$#" >& /dev/null < /dev/null &
I think you need to do setsid "$#" >& /dev/null & wait so that the controlling terminal does not disappear before setsid manages to fork the child.
UPDATE
It seems to me that this works both on command line and as argument of -c:
(setsid tail -F /var/log/messages >& /dev/null) & disown
You are trying to create a UNIX daemon process (i.e., a process that has no controlling terminal and that is its own session leader). The setsid command should do this for you, but you are responsible for closing all file descriptors that are open on the terminal you are abandoning. This can be done by redirecting them to /dev/null or using the shell's syntax for closing file descriptors (e.g., 2>&- and 0<&- in Bash).

How to include nohup inside a bash script?

I have a large script called mandacalc which I want to always run with the nohup command. If I call it from the command line as:
nohup mandacalc &
everything runs swiftly. But, if I try to include nohup inside my command, so I don't need to type it everytime I execute it, I get an error message.
So far I tried these options:
nohup (
command1
....
commandn
exit 0
)
and also:
nohup bash -c "
command1
....
commandn
exit 0
" # and also with single quotes.
So far I only get error messages complaining about the implementation of the nohup command, or about other quotes used inside the script.
cheers.
Try putting this at the beginning of your script:
#!/bin/bash
case "$1" in
-d|--daemon)
$0 < /dev/null &> /dev/null & disown
exit 0
;;
*)
;;
esac
# do stuff here
If you now start your script with --daemon as an argument, it will restart itself detached from your current shell.
You can still run your script "in the foreground" by starting it without this option.
Just put trap '' HUP on the beggining of your script.
Also if it creates child process someCommand& you will have to change them to nohup someCommand& to work properly... I have been researching this for a long time and only the combination of these two (the trap and nohup) works on my specific script where xterm closes too fast.
Create an alias of the same name in your bash (or preferred shell) startup file:
alias mandacalc="nohup mandacalc &"
Why don't you just make a script containing nohup ./original_script ?
There is a nice answer here: http://compgroups.net/comp.unix.shell/can-a-script-nohup-itself/498135
#!/bin/bash
### make sure that the script is called with `nohup nice ...`
if [ "$1" != "calling_myself" ]
then
# this script has *not* been called recursively by itself
datestamp=$(date +%F | tr -d -)
nohup_out=nohup-$datestamp.out
nohup nice "$0" "calling_myself" "$#" > $nohup_out &
sleep 1
tail -f $nohup_out
exit
else
# this script has been called recursively by itself
shift # remove the termination condition flag in $1
fi
### the rest of the script goes here
. . . . .
the best way to handle this is to use $()
nohup $( command1, command2 ...) &
nohup is expecting one command and in that way You're able to execute multiple commands with one nohup

How to change argv0 in bash so command shows up with different name in ps?

In a C program I can write argv[0] and the new name shows up in a ps listing.
How can I do this in bash?
You can do it when running a new program via exec -a <newname>.
Just for the record, even though it does not exactly answer the original poster's question, this is something trivial to do with zsh:
ARGV0=emacs nethack
I've had a chance to go through the source for bash and it does not look like there is any support for writing to argv[0].
I'm assuming you've got a shell script that you wish to execute such that the script process itself has a new argv[0]. For example (I've only tested this in bash, so i'm using that, but this may work elsewhere).
#!/bin/bash
echo "process $$ here, first arg was $1"
ps -p $$
The output will be something like this:
$ ./script arg1
process 70637 here, first arg was arg1
PID TTY TIME CMD
70637 ttys003 0:00.00 /bin/bash ./script arg1
So ps shows the shell, /bin/bash in this case. Now try your interactive shell's exec -a, but in a subshell so you don't blow away the interactive shell:
$ (exec -a MyScript ./script arg1)
process 70936 here, first arg was arg1
PID TTY TIME CMD
70936 ttys008 0:00.00 /bin/bash /path/to/script arg1
Woops, still showing /bin/bash. what happened? The exec -a probably did set argv[0], but then a new instance of bash started because the operating system read #!/bin/bash at the top of your script. Ok, what if we perform the exec'ing inside the script somehow? First, we need some way of detecting whether this is the "first" execution of the script, or the second, execed instance, otherwise the second instance will exec again, and on and on in an infinite loop. Next, we need the executable to not be a file with a #!/bin/bash line at the top, to prevent the OS from changing our desired argv[0]. Here's my attempt:
$ cat ./script
#!/bin/bash
__second_instance="__second_instance_$$"
[[ -z ${!__second_instance} ]] && {
declare -x "__second_instance_$$=true"
exec -a MyScript "$SHELL" "$0" "$#"
}
echo "process $$ here, first arg was $1"
ps -p $$
Thanks to this answer, I first test for the environment variable __second_instance_$$, based on the PID (which does not change through exec) so that it won't collide with other scripts using this technique. If it's empty, I assume this is the first instance, and I export that environment variable, then exec. But, importantly, I do not exec this script, but I exec the shell binary directly, with this script ($0) as an argument, passing along all the other arguments as well ($#). The environment variable is a bit of a hack.
Now the output is this:
$ ./script arg1
process 71143 here, first arg was arg1
PID TTY TIME CMD
71143 ttys008 0:00.01 MyScript ./script arg1
That's almost there. The argv[0] is MyScript like I want, but there's that extra arg ./script in there which is a consequence of executing the shell directly (rather than via the OS's #! processing). Unfortunately, I don't know how to get any better than this.
Update for Bash 5.0
Looks like Bash 5.0 adds support for writing to special variable BASH_ARGV0, so this should become far simpler to accomplish.
(see release announcement)
( exec -a foo bash -c 'echo $0' )
ps and others inspect two things, none of which is argv0: /proc/PID/comm (for the "process name") and /proc/PID/cmdline (for the command-line). Assigning to argv0 will not change what ps shows in the CMD column, but it will change what the process usually sees as its own name (in output messages, for example).
To change the CMD column, write to /proc/PID/comm:
echo -n mynewname >/proc/$$/comm; ps
You cannot write to or modify /proc/PID/cmdline in any way.
Process can set their own "title" by writing to the memory area in which argv & envp are located (note that this is different than setting BASH_ARGV0). This has the side effect of changing /proc/PID/cmdline as well, which is what some daemons do in order to prettify (hide?) their command lines. libbsd's setproctitle() does exactly that, but you cannot do that in Bash without support of external tools.
I will just add that this must be possible at runtime, at least in some environments. Assigning $0 in perl on linux does change what shows up in ps. I do not know how that is implemented, however. If I can find out, i'll update this.
edit:
Based on how perl does it, it is non-trivial. I doubt there is any bask built in way at runtime but don't know for sure. You can see how perl does sets the process name at runtime.
Copy the bash executable to a different name.
You can do this in the script itself...
cp /bin/bash ./new-name
PATH=$PATH:.
exec new-name $0
If you are trying to pretend you are not a shell script you can rename the script itself to something cool or even " " (a single space) so
exec new-name " "
Will execute bash your script and appears in the ps list as just new-name.
OK so calling a script " " is a very bad idea :)
Basically, to change the name
bash script
rename bash and rename the script.
If you are worried, as Mr McDoom. apparently is, about copying a binary to a new name (which is entirely safe) you could also create a symlink
ln -s /bin/bash ./MyFunkyName
./MyFunkyName
This way, the symlink is what appears in the ps list. (again use PATH=$PATH:. if you dont want the ./)

Resources