0

According to this answer to How to Hide PID and Exit Status When Running a Background Command in Terminal, using (some command &) one can stop the PID of the background process from appearing in a terminal.

I would like to stop a background process started with (some command &) that was started by a function in a .zshrc file.

How can I stop that process without the PID number ? If I use $!, the PID is 0 and it does not seem that that PID can be used to stop the background process.

6
  • 1
    "in the same script" – Bash interpreting a script is usually non-interactive, so job control is off by default, so starting an asynchronous process does not print a message to the terminal, so there is no need for parentheses to silence the message, so some command & should work, so $! should work. Why do you think you need the parentheses in the first place? Do you enable job control (set -m) in your script? Or do you let an interactive shell interpret the script? Why? Commented Sep 16, 2024 at 3:39
  • @KamilMaciorowski Thank you. I am not sure if it is interactive. I am calling a function defined in a .zshrc file. That function starts a background process. When I used command & the terminal would output the process id of the background command. Commented Sep 16, 2024 at 3:46
  • (1) So it's not in a script, it's in a shell that shows you prompts and you interact with it; right? (2) .zshrc is specific to zsh. Is your shell zsh? Why did you use the tag bash then? Commented Sep 16, 2024 at 3:48
  • @KamilMaciorowski Thank you. I was speeking too loosely putting together scripts and function in a .zshrc file maybe assuming similar behavior. I am not really looking for a zsh specific solution althought I would upvote one. I will remove bash and specify that in my problem I am considering a function in a .zshrc file. Commented Sep 16, 2024 at 3:55
  • Does your some command print to its stdout and do you really want to see this? There may be a relatively simple solution if the output of the command may be discarded. Commented Sep 16, 2024 at 4:06

2 Answers 2

3

Technically, (some command &) runs some command asynchronously in a sub-shell, so it's not inserted in the job table of the (main) shell, and $! is set to the pid of that process but only within that subshell.

The shell has no knownledge of that process as it wasn't the one who started it, so you'll need to identify the process (or processes as some command could have spawned more processes) some other way.

pgrep and pkill can come handy for that as it can search processes based on several of its attributes.

Unless the process detached itself from the terminal, it will still have $TTY (set automatically by zsh to the path of the controlling terminal device) as its controlling terminal.

pkill -xft ${TTY#/dev/} 'some command' 

Would kill the processes with $TTY as their controlling terminal and whose exact full command line is some command.

You can use pgrep -l instead of pkill first to see which pid(s) that finds only with their command line.

The above only works if some is a file that that process executed with some and command as arguments. If some command is a shell construct such as a while loop as you indicate in comment, then that will just be a child process of your shell's running it and its argument list (the thing pgrep -fx matches on) will be the same as that of the current shell, likely something like zsh, -zsh or /usr/bin/zsh.

If that's the only process running unattended in that shell session in your terminal, then you can do:

pkill -t ${TTY#/dev/} 

On FreeBSD/macos, pkill will automatically exclude the ancestor processes of itself unless you add the -a option, so your shell will not be killed. On Linux-based systems using pkill from procps-ng, you'll want to pass the -A option to explicitly exclude the ancestors.

3
  • Thank you for your very informative and clear answer. I am using linux and at the end of the function defined in my .zshrc file that calls the while loop in the background I tried both pkill -t ${TTY#/dev/} and pkill -tA ${TTY#/dev/}. With pkill -t ${TTY#/dev/} it seems like the while loop stops after all the commands of the function ran. The terminal itself does not stop but I get an error from my installation of github.com/romkatv/powerlevel10k that says "gitstatus_query_p9k_:print:68: write error: broken pipe". Commented Sep 16, 2024 at 7:55
  • With pkill -tA ${TTY#/dev/} the while loop seems to continue. Commented Sep 16, 2024 at 7:55
  • For the moment I am using your other suggestion that you wrote for another question unix.stackexchange.com/a/157256/542871 with the syntax (some command )&! PID=$! then kill "$PID" which seems to work in zsh (and apparently not in bash) but I do not know why and do not know how to find documentation about that syntax. That has the disadvantage that it might not work if I try another shell in the future. Commented Sep 16, 2024 at 8:01
0

execute the following command

sudo ps -ef | \grep "some command" 

Over here "some command" refers to the command that was started. In the output the first column is the PID. To kill the process use the following command

kill -n 9 <<PID>> 
4
  • 2
    Unless the process is rogue, kill -n 9 should not be the first choice. Commented Sep 16, 2024 at 4:27
  • Thank you, "some command" is actually a while loop and when I used ps aux it seemed like the process name was that of the terminal that launched it so in my case one of the many zsh (like bash) processes. I think I can identify which one of the zsh processes it is by the status column in ps aux as it has N (for nice) but that might cause an issue if ever there is another zsh process with an N status Commented Sep 16, 2024 at 4:29
  • In any case I can upvote your answer as it does work in a lot of cases but I do not think the sudo is necessary in general and I think it is best to avoid using that when not necessary so I am reluctant to upvote your answer because of the sudo Commented Sep 16, 2024 at 4:30
  • There is a command which I will mention later that can both search for processes by name and remove them but doing that one removes all processes that match the string before verifying what the matches are which can lead one to remove process that should not have been removed. I prefer using pgrep which finds processes that match a string and then use pkill, the process I said I would mention later, which has the ability to remove processes that match a string. I use pgrep "part of name" then if I want to remove all of the matches I use pkill "part of name". Commented Sep 16, 2024 at 6:47

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.