简体   繁体   中英

Bash, timeout 'ls | wc -l'

I have searched this site and google on how to timeout a bash command if it takes to long to run and have tried multiple different approaches and cant seem to get any to work.

Specifically, I need to time out 'ls | wc -l' if it takes longer than 10 seconds to run; however, I will be running the 'ls | wc -l' on multiple directories and don't want to wait 10 seconds for each one if it only takes a second to finish.

I have tried to following which only seems to kind of work, but I still have to wait to full 10 seconds when 'ls | wc -l' finishes before the 10-second sleep.

ls /file/path/to/count/ | wc -l &
pidsave=$!
sleep 1
if [ -e /proc/$pidsave ]; then
kill $pidsave; echo $?
echo 'ls command was timed out'
else
echo 'ls command completed'
fi

You can use the timeout command:

if timeout 10 ls /file/path/to/count/ | wc -l; then
    echo "Successful!!"
else
    echo "Timed Out!!"
fi

Check man timeout .

The previously posted answer also did not work for me. The problem there is that if tests the result of wc -l , not the actual timeout .

To avoid this, you can either wrap the commands in a function, put the commands into an excutable file, or un-chain the commands and execute them separately, depending on what do you actually need to do with the output in your context.

Option 1 - function

The function has to be exported to subshells and then executed under a subshell.

#!/bin/bash
function my_ls {
    ls | wc -l
}

export -f my_ls
timeout 10 bash -c my_ls
if [ $? -eq 0 ]; then
    echo "cmd finished in time"
else
    echo "cmd did NOT finish in time"
fi

Option 2 - file

Put ls | wc -l ls | wc -l into some file (eg /tmp/ls.sh) and make it executable ( chmod +x /tmp/ls.sh ). Use that file as the parameter for timeout command:

#!/bin/bash
MY_FILE="/tmp/ls.sh"
echo "ls | wc -l" > $MY_FILE
chmod +x $MY_FILE

timeout 10 $MY_FILE
if [ $? -eq 0 ]; then
    echo "cmd finished in time"
else
    echo "cmd did NOT finish in time"
fi

Option 3 - un-chained

Assuming the ls is the command that is prone to taking a long time to finish in this example, run just that first. Possible output is stored in a variable (assuming the command does not time out) that you can use however you want later on:

#!/bin/bash
LIST="$(timeout 10 ls)"
if [ $? -eq 0 ]; then
    echo "cmd finished in time"
    echo $(wc -l <<< $LIST)
else
    echo "cmd did NOT finish in time"
fi

With the help of Jiri, Option 3 did the trick. Here is the exact code I am using in case anyone is interested. I wrap this in a for statement and provided a header of Count in a light blue, TIMED OUT!!! in Red, and the actual count in Green.

echo -e "\e[1;4;36mCount\e[0m" >> $LOGPATH
for c in $DIR1 $DIR2 $DIR3 $DIR4; do

LIST=$(timeout 2 ls $c &>/dev/null; echo $?)
if [ $LIST != 0 ]; then
    echo -e $c "\e[1;31mTIMED OUT!!! \e[0m" >> $LOGPATH
else
  echo -e $c "\e[1;32m$(ls $c |wc -l) \e[0m" >> $LOGPATH
fi

done

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM