protonscr

Stop client after exiting game

steamopen Feature RequestreviewedSteam client
ValveSoftware/steam-for-linux#1721 · opened 2013-02-14 by Necoro · updated 2025-08-21 · 21 comments · github
NNecoro 2013-02-14 github

When starting a game from the commandline via steam steam://rungameid/123456, steam does not exit when game is closed. In Issue #255, this was dubbed 'intended behavior'. Please add at least an option that closes steam afterwards, so one can build scripts around steam. This holds especially, since Steam does not use the systray but only this ubuntu-specific "indicator"-thingy, and hence besides killing there is no way of closing steam afterwards.

(Btw: On windows steam.exe -applaunch 123456 used to work in the correct manner. But now the -applaunch behavior seems to be removed completely :( )

Nnpic 2014-01-01 github

I just faced this and I totally agree with you. This "normal behavior" is weird and counter-intuitive for one who just wants to run a specific game. I suggest to add another steam:// endpoint (like "steam://rungameidandexit/123456"), so nobody's workflow will be broken.

EEnverex 2016-06-29 github

Been a few years now guys, any movement on this? It's a big issue when trying to use Steam games with a frontend as Steam forks the game and remains running forever, so the launcher never realises the game itself has finished.

Ffbt 2016-08-07 github

Would also like a flag for this. Don't like to run the steam client when I don't need it, and closing it every time after -applaunch is tedious.

Jjsommr 2016-10-27 github

Pretty annoying on windows too. Would be great with a command line option here.

Eejthill 2017-02-24 github

Ran into this when I was googling the same issue. I'm using a "Attract-Mode" as my front end, and I want it to know when the app closes.

If you're on Linux, here's a hack I'm using to accomplish this. It's a bash script that will note the name of the current active window (your launcher application or terminal or whatever), launch the steam app, wait a configurable amount of time for it to actually open a new window, then check the name of the active window every second. Once the name of the active window matches your original launcher application (after the game exists), the script will exit.

Requirements
sudo apt-get install xdotool

Usage
./steamlaunch.sh <app_id> <launch_timeout>

./steamlaunch.sh content

app=$1
launchtimeout=$2
#If launchtimeout not specified, use default of 5
if [[ -z "$launchtimeout" ]]; then
  launchtimeout=5
fi
#Lookup current active window
launchwindow=$(xdotool getwindowfocus getwindowname)
#Launch Application
steam -silent -applaunch $app &
#Give steam time to start and launch a new window
sleep $launchtimeout
#Wait until the focus returns to the original window
while [ "$focus" != "$launchwindow" ]
do
  focus=$(xdotool getwindowfocus getwindowname)
  sleep 1
done
exit

This will keep the script "executing" until the game exits. If you want to completely kill steam, add the following to the end of the script right before "exit".
pgrep steam | while read pid; do kill $pid; done

Super hack? Yes. Does it work in my case? Yes.

I'm sure this won't handle everyones' use cases, but it may be a start. Hopefully this will be appropriately supported in the future.

?ghost 2017-06-08 github

This is my solution.

#!/bin/bash
#
# script to exit steam after game exit
#
# sets function to run when game ends and this script is exiting,
# waits 5 seconds and kills steam
#
# put this script somewhere in PATH, call it steam-exit
# and put steam-exit %command% in games launch options
#
trap game_exit EXIT

function game_exit {
	sleep 5
	pkill steam
}

# execute game command
"$*"
SSpacehopper1234 2018-01-09 github

Just installed Steam recently, and it was annoying me. I made this batch file for BF1 a couple of years ago to shut down Origin after game exit, and it works for Steam also. Its not pretty, but it works.
Create batch file.
Add:-

@ECHO OFF
START "" "C:\Program Files (x86)\Steam\steamapps\common\YourGame\YourGame.exe"
TIMEOUT /t 20
:RUNNING
tasklist|findstr YourGame.exe > nul
if %errorlevel%==1 timeout /t 5 & taskkill /F /IM Steam.exe /T & GOTO ENDLOOP
timeout /t 2
GOTO RUNNING
:ENDLOOP

It waits 20 secs to connect to and update Steam, (or set longer if your'e having connection issues) and then just runs a loop to check if your game is still running, and then when it isnt, to kill Steam. Simples.
I create a shortcut to the batch file and set it to run minimised. (although i think you can use 'SET MIN' or something.

EDIT: Apologies, came here just looking to see if anyone else had same issue as me. Just noticed its a Linux forum :)

Ssreksten 2018-01-29 github

Same here. I added the Steam client to EmulationStation, but until Steam closes I am not able to get back to Emulationstation after I finished playing the game.
This thread is nearly 5 years old, it is possible to have a switch that allows Steam to close automatically after a game exits?

AAndydeCleyre 2018-08-03 github

For those launching a game with primusrun or similar, the client keeps the video card engaged without much to indicate its presence.

Ttixlegeek 2020-10-10 github

The only option i trust:

ps aux | grep steam | grep -v grep | awk '{print "kill " $2}' | sh

Eekatonb 2021-05-07 github

My workaround:

launch a game with the following script (steam_launcher.sh)

#!/bin/bash

APP_ID=$1
SLEEP=2

steam -silent -applaunch $APP_ID > /tmp/steam.out 2>&1 &

STOP=0

while [ $STOP -lt 1 ];do
  sleep $SLEEP
  STOP=$(tail -100 /tmp/steam.out | grep "Exiting app $APP_ID" -c)
done

steam -shutdown

usage: steam_launcher.sh APP_ID

NNecoro 2021-05-07 github

@ekatonb: Out of curiosity, the following should work the same without the sleep and the problem of something scrolling by in the last 100 lines:

#!/bin/bash

APP_ID=$1

steam -silent -applaunch $APP_ID 2>&1 |  while read line; do
       if [[ $line == *"Exiting app $APP_ID"* ]]; then 
         steam -shutdown
         break
       fi
    done
Eekatonb 2021-05-11 github

@ekatonb: Out of curiosity, the following should work the same without the sleep and the problem of something scrolling by in the last 100 lines:

#!/bin/bash

APP_ID=$1

steam -silent -applaunch $APP_ID 2>&1 |  while read line; do
       if [[ $line == *"Exiting app $APP_ID"* ]]; then 
         steam -shutdown
         break
       fi
    done

@Necoro: even better, thanks!

Kki9us 2022-06-26 github

I think they changed the logging and it now says Game process removed: AppID XXXX. So this is the script I'm using:

#!/bin/bash
# steamlaunch

steam -silent -applaunch $1 2>&1 | tee >(
  while read line; do
    if [[ $line == *"Game process removed: AppID $1"* ]]; then
      sleep 10; steam -shutdown; break
    fi
  done
)

Other edits I made (apply to your taste):

  • Replaced $APP_ID with $1
  • Added a sleep to ensure the game finished its cleanup
  • Used tee >( ... ) to allow the output to continue to STDOUT.
Mmikkorantalainen 2023-09-30 github
  • Added a sleep to ensure the game finished its cleanup

Doesn't steam -shutdown already wait for the game to finish before exiting?

RRikj000 2023-10-22 github

@ki9us

I think they changed the logging and it now says Game process removed: AppID XXXX.

I think they changed the logging once again,
can't find Game process removed: AppID XXX anymore,
So I went with Uploaded AppInterfaceStats to Steam which appears to work as well.
(Only tested with game: The Binding of Isaac)

Final script:

#!/bin/bash
# steamlaunch

steam -silent -applaunch $1 2>&1 | tee >(
  while read line; do
    if [[ $line == *"Uploaded AppInterfaceStats to Steam"* ]]; then
      sleep 10; steam -shutdown; break
    fi
  done
)

@mikkorantalainen

Doesn't steam -shutdown already wait for the game to finish before exiting?

It doesn't, if you add -shutdown, steam will close without waiting for the game to finish.

KKex1016 2024-08-02 github

None of the scripts worked for me, as soon as I pipe any of the output it becomes just the lines that steam.sh and setup.sh spew out, which contain no useful information about the game. :/

I made a script with tmux that seems to do the job well:

#!/bin/bash
# steamlaunch

APP_ID=$1

echo "Starting game $APP_ID"
touch /tmp/steam_output.log

# Create a new tmux session and run the Steam command inside it
tmux new-session -d -s steam_session "steam -silent -applaunch $APP_ID"

# Capture the tmux session's output to a log file
tmux pipe-pane -o -t steam_session "cat > /tmp/steam_output.log"

# Monitor the log file for the specific game stop message
tail -f /tmp/steam_output.log | while read -r line; do
    if [[ $line == *"Removing process"* && $line == *"for gameID $APP_ID"* ]]; then
        echo "The game exited, quitting Steam."
        sleep 5 # Just in case.
        steam -shutdown
        tmux kill-session -t steam_session
        break
    fi
done

echo "Done!"
rm /tmp/steam_output.log
Kki9us 2024-08-02 github

I revisited this (I don't play games enough to recognize that my script stopped working). I saw a line in the command output reading Removing process * for gameID $1, which seems perfect. The only problem is that most of the output won't pass through the pipe. It took me a long time to figure out why: steam isn't writing to STDOUT or STDERR; It's manipulating the pty directly, like how ncurses works. That's why tmux or screen will work, but ordinary pipes won't.

I'm starting to think the steam team is monitoring this thread and intentionally coming up with new ways to force us to keep steam running in the background to track us. Dear Steam Devs: Give up. You won't win this war.

Well here's a way to capture all the terminal output and force it to STDOUT using the script command:

script -qac 'steam -silent -applaunch XXXXXX' /dev/stdout

script is cleaner imo than screen/tmux imo and comes standard with most distros. So my new working script is:

#!/bin/bash
# steamlaunch
# https://github.com/ValveSoftware/steam-for-linux/issues/1721

script -qac "steam -silent -applaunch ${1}" /dev/stdout | tee >( 
    while read line; do
        if [[ "${line}" == *"Removing process "* ]] \
        && [[ "${line}" == *" for gameID ${1}"* ]]; then
            steam -shutdown; break
        fi
    done
)

Other improvements:

  • Added curly braces and quotes to most variables
  • Removed the sleep which was, in fact, not necessary.

Note that it won't work if steam was already running when the script was launched because steam will only send the output to the pty that initiated it.

Your move, devs!

GGalcian79 2024-08-06 github

This is my fixed script:

APPID=
script -qac "steam -silent -applaunch $APPID" /dev/stdout 2>&1 | tee >(
while read line; do
if [[ $line == *"Uploaded AppInterfaceStats to Steam"* ]]; then
sleep 10; steam -shutdown; break
fi
done
)

The sleep should be useful if you don't have a fast connection, to avoid Steam abruptly shutting down while still syncing to the cloud, when you issue the shutdown command. But i've never really tested it.

LLokito23 2025-08-21 github

Currently, AppID <Insert AppID here> state changed : Fully Installed, after AppID <Insert AppID here> state changed : Fully Installed,App Running, shows that the game was closed.

Seemingly, Steam has still not implemented a way to stop the client after the game exits, and keeps changing the log message that describes that the game closed.

This is bad for other launchers and front-ends, which depend on Steam to either report that the game was closed, or depend on Steam itself closing. Currently, this is already affecting Pegasus Frontend, and there are probably more launchers having the same issue.

I'd suggest adding a new command line argument, that makes the client close when the game stops. It would need to request a Steam URL or AppID in order to run the game, and fail if there is none. It could be named -gameonly, though the name could be improved.

LLokito23 2025-08-21 github

It could also get fixed by the following:

While Steam is in the background, when running a game with the steam command, the command stays running until the game exits, which would allow the Steam client to stay open, while allowing launchers to check if the game has closed, by waiting for the command to finish.