簡體   English   中英

修改正在運行的Bash腳本中的變量

[英]Modify a variable in a running Bash script

我有一個處理幾年數據的bash腳本,因此腳本可能需要一周的時間才能完成。 為了加快這個過程,我使用多線程,並行運行多個實例(每個實例= 1天的數據)。 每個實例占用1個CPU,因此我可以運行與可用CPU一樣多的實例。 當我在一個功能強大的服務器上運行該進程時,我正在與其他人共享,有時我可能會有更多或更少的CPU可用。 我目前的腳本是:

#!/bin/bash
function waitpid {
   #Gather the gLABs PID background processes (Maximum processes in 
   #background as number of CPUs)
   NUMPIDS=`jobs -p|awk 'END {print NR}'`
   #A while is set because there seems to be a bug in bash that makes 
   #sometimes the "wait -n" command
   #exit even if none of provided PIDs have finished. If this happens, 
   #the while loops forces the 
   #script to wait until one of the processes is truly finished
   while [ ${NUMPIDS} -ge ${NUMCPUS} ]
   do
     #Wait for gLAB processes to finish
     PIDS="`jobs -p|awk -v ORS=" " '{print}'`"
     wait -n ${PIDS} >/dev/null 2>/dev/null
     NUMPIDS=`jobs -p|awk 'END {print NR}'`
   done
}
NUMPCUS=10
for(...) #Loop for each day
do
   day=... #Set current day variable
   #Command to execute, put in background
   gLAB_linux -input ${day}folder/${day}.input -output ${day)outfolder/${day}.output &        
   #Wait for any process to finish if NUMCPUS number of processes are running in background
   waitpid 
done

因此,我的問題是:如果此腳本正在運行,是否有任何方法可以在不停止腳本的情況下將變量NUMCPUS更改為任何值(例如NUMCPUS = 23)? 如果可能的話,我更喜歡一種不涉及讀取或寫入文件的方法(如果可能的話,我喜歡將臨時文件減少到0)。 我不介意它是否是一個“hackish”過程,例如本答案中描述的方法。 實際上,我在gdb中嘗試了類似於該答案的命令,但是它沒有用,我在gdb中遇到了以下錯誤(並且還使進程崩潰):

(gdb) attach 23865
(gdb) call bind_variable("NUMCPUS",11,0)
'bind_variable' has unknown return type; cast the call to its declared return type
(gdb) call (int)bind_variable("NUMCPUS",11,0)
Program received signal SIGSEGV, Segmentation fault

EDIT1:對腳本的一些評論:

  • gLAB_linux是單核處理程序,不知道NUMCPUS變量
  • 每個gLAB_linux執行大約需要5個小時才能完成,因此bash腳本大部分時間都在wait -n內部休眠。
  • NUMCPUS必須是腳本的局部變量,因為可能存在另一個並行運行的腳本(僅更改給gLAB_linux的參數)。 因此,NUMCPUS不能是環境變量。
  • 訪問NUMCPUS的唯一進程是bash腳本

編輯2:在@Kamil回答之后,我添加了我的建議,從文件中讀取CPU的數量

function waitpid {
   #Look if there is a file with new number of CPUs
   if [ -s "/tmp/numCPUs_$$.txt" ]
   then
     TMPVAR=$(awk '$1>0 {print "%d",$1} {exit}' "/tmp/numCPUs_$$.txt")
     if [ -n "${TMPVAR}" ]
     then
       NUMCPUS=${TMPVAR}
       echo "NUMCPUS=${TMPVAR}"
     fi
     rm -f "/tmp/numCPUs_$$.txt"
   fi

   #Gather the gLABs PID background processes (Maximum processes in 
   #background as number of CPUs)
   NUMPIDS=`jobs -p|awk 'END {print NR}'`
   #A while is set because there seems to be a bug in bash that makes 
   #sometimes the "wait -n" command
   #exit even if none of provided PIDs have finished. If this happens, 
   #the while loops forces the 
   #script to wait until one of the processes is truly finished
   while [ ${NUMPIDS} -ge ${NUMCPUS} ]
   do
     #Wait for gLAB processes to finish
     PIDS="`jobs -p|awk -v ORS=" " '{print}'`"
     wait -n ${PIDS} >/dev/null 2>/dev/null
     NUMPIDS=`jobs -p|awk 'END {print NR}'`
   done
}

最好的方法是修改bash腳本,以便它知道你改變了值。 從gdb會話中修改環境變量 - 這只是侵入性的,並且主要是丟棄其他開發人員的工作。

下面我使用名為/tmp/signal_num_cpus的文件。 如果該文件不存在,則腳本使用NUMCPUS值。 如果文件存在,則會讀取內容並相應地更新NUMCPUS的數量,然后打印一些numcpus已更改為文件的通知。 如果文件存在且不包含有效數字(例如,在預定義范圍或smth中),則會在文件中輸出一些錯誤消息。 另一方通知一切正常或發生了一些不好的事情

#!/bin/bash

is_not_number() { 
    (( $1 != $1 )) 2>/dev/null
}

# global variable to hold the number of cpus with a default value
NUMCPUS=${NUMCPUS:=5}
# this will ideally execute on each access to NUMCPUS variable
# depending on content
get_num_cpus() { 
   # I tell others that NUMCPUS is a global variable and i expect it here
   declare -g NUMCPUS
   # I will use this filename to communicate
   declare -r file="/tmp/signal_num_cpus"
   # If the file exists and is a fifo...
   if [ -p "$file" ]; then
       local tmp
       # get file contents
       tmp=$(<"$file")
       if [ -z "$tmp" ]; then
           #empty is ignored
           :;
       elif is_not_number "$tmp"; then
           echo "Error reading a number from $file" >&2
           echo "error: not a number, please give me a number!" > "$file"
       else
           # If it is ok, update the NUMCPUS value
           NUMCPUS=$tmp
           echo "ok $NUMCPUS" > "$file"  # this will block until other side starts reading
       fi
   fi
   # last but not least, let's output it
   echo "$NUMCPUS"
}

# code duplication is the worst (ok, sometimes except for databases frameworks)
get_num_bg_jobs() {
    jobs -p | wc -l
}

waitpid() {
   while 
         (( $(get_num_bg_jobs) >= $(get_num_cpus) ))
   do
         wait -n
   done
}

# rest of the script

NUMPCUS=10
for(...) #Loop for each day
do
   day=... #Set current day variable
   #Command to execute, put in background
   gLAB_linux -input "${day}folder/${day}.input" -output "${day)outfolder/${day}.output" &        
   #Wait for any process to finish if NUMCPUS number of processes are running in background
   waitpid 
done

更改值腳本可能如下所示:

#!/bin/bash

# shared context between scripts
declare -r file="/tmp/signal_num_cpus"

mkfifo "$file"

echo 1 > "$file" # this will block until other side will start reading

IFS= read -r line < "$file"

case "$line" in
ok*) 
     read _ numcpus <<<"$line"
     echo "the script changed the number of numcpus to $numcpus"
     ;;
*)
     echo "the script errored with $error"
     ;;
esac

rm "$file"

分數:

  • 定義函數的正確方法是func() { :; } func() { :; }使用function func { }是從KSH采取財產以后並且被支撐為擴展。 使用func() {}
  • 最好使用算術擴展(( ... ))進行數字比較和處理。
  • 使用反引號`進行命令替換$( ... )已棄用。

GNU Parallel 2018的第7.1章介紹了如何在運行https://zenodo.org/record/1146014時更改要使用的線程數

echo 50% > my_jobs
/usr/bin/time parallel -N0 --jobs my_jobs sleep 1 :::: num128 &
sleep 1
echo 0 > my_jobs
wait

所以你只需將--jobs的參數放入my_jobs ,GNU Parallel將在每個完成的作業后讀取它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM