讀取每個目錄並在 Bash 腳本中執行操作

讀取每個目錄並在 Bash 腳本中執行操作

我有這個目錄結構/var/sync

# tree -d /var/sync/
/var/sync/
├── sync_bi
│   ├── replicator => Replicator.php
│   sync.php 
├── sync_pfizer
│   ├── replicator => Replicator.php
│   sync.php
│ replicator.sh
│ sync.sh

如您所看到的,每個sync_*目錄上都有一個名為 的腳本sync.php,並且每個replicator目錄上都有一個名為 的腳本Replicator.php。我需要每晚運行這些腳本。Replicator.php應在 00:00 AM 運行,並sync.php應在 12:30 AM 運行。我知道這裡正確的路徑顯然是一個 cron 作業。我製作了兩個腳本replicator.shsync.sh共享幾乎相同的程式碼,主要區別在於腳本名稱。請參閱下面的程式碼:

replicator.sh
#! /bin/bash
cd $1
/usr/bin/php Replicator.php

sync.sh
#! /bin/bash
cd $1
/usr/bin/php sync.php

然後我將每個添加到 cronjob 中,如下所示:

0 0 * * * /var/sync/replicator.sh /var/sync/sync_bi/replicator/
0 30 * * * /var/sync/sync.sh /var/sync/sync_bi/
0 2 * * * /var/sync/replicator.sh /var/sync/sync_pfizer/replicator/
30 2 * * * /var/sync/sync.sh /var/sync/sync_pfizer/

所有這些解釋背後的想法是,透過將腳本更改為僅允許我在一次 cronjob 呼叫中執行所有操作並保留更少的檔案來維護的腳本來優化我的環境。所以我做了一些研究,發現了一些有用的主題:

  • 透過使用find some/dir -type f -execdir somecommand {} \;是實現與我所做的相同的事情的正確方法cd
  • 並且通過移動到根路徑下的每個目錄 - 在我的例子中/var/sync

     for D in ./*; do
        if [ -d "$D" ]; then
            cd "$D"
            run_something
            cd ..
        fi
    done
    

這是我正在談論的偽代碼(以防不夠清楚):

read tree structure at /var/sync
foreach (directory in root+path)
    if (directory_name is replicator)
        cd replicator/
        /usr/bin/php replicator.php
    else if (directory_name is sync_*)
        cd sync_*/
        /usr/bin/php sync.php
endforeach

我不是 bash 專家,所以我請求一些幫助來將偽代碼翻譯成功能性的東西,讓我能夠實現我正在尋找的東西,有什麼建議和/或幫助嗎?

更新

也許我錯了,但我根本沒有遵循你們試圖做的事情,所以這裡是事件的順序:

# Run Replicator at mid day and mid night
# this two can run at same time since they are pushing data to different DBs
/var/sync/sync_bi/replicator/Replicator.php
/var/sync/sync_pfizer/replicator/Replicator.php

# Run sync at mid day and mid night
# this two should run one first and the second after 30 min since they are 
# pushing data to the same DBs and second depends on first one
/var/sync/sync_bi/sync.php
/var/sync/sync_pfizer/sync.php

有了這些資訊(很抱歉首先沒有弄清楚這一點)您的解決方案應該如何運作?因為你現在已經有了它們,或者你需要改變任何東西來解決這個問題?

答案1

這會將您的偽代碼轉換為 bash:

#!/bin/bash
cd /var/sync
for dir in sync*/ ; do
    ( # Subshell to make "cd" not global.
        cd "$dir"
        [[ -f sync.php ]] && /usr/bin/php sync.php
        if [[ -d replicator ]] ; then
            cd replicator
            [[ -f replicator.php ]] && /usr/bin/php replicator.php
        fi

    )
done

不過,它同時運行sync.php和replicator.php,但您最初想在不同的時間呼叫它們。也許像

#!/bin/bash

run=$1

cd /var/sync
for dir in sync*/ ; do
    ( # Subshell to make "cd" not global.
        cd "$dir"
        [[ -f "$run" ]] && /usr/bin/php "${run##*/}"
    )
done

從 cron 調用,就像

0 30 * * * /path/to/script.sh sync.php
0 0 * * * /path/to/script.sh replicator/Replicator.php

${run##*/}刪除最後一個之前的所有內容/,即僅傳回基本名稱。

更新

如果您想如下運行腳本:

0:00 sync_a/sync.php
0:30 sync_a/replicator/Replicator.php
1:00 sync_b/sync.php
1:30 sync_b/replicator/Replicator.php
2:00 sync_c/sync.php
...

等等,你可以在每次改變目錄結構時重新產生 crontab:

#!/bin/bash
add=$1
php=/usr/bin/php
hour=0
minute=0

update_time () {
    (( minute += add ))
    if (( minute >= 60 )) ; then
        (( hour += minute / 60 ))
        (( minute %= 60 ))
    fi
    if (( hour > 11 )) ; then
        echo "Can't fit into a day!"
        exit 1
    fi
}
cd /var/sync

for dir in sync*/ ; do
    if [[ -f "$dir"/sync.php ]] ; then
        echo $minute $hour 0 0 0 "$php" /path/to/run.sh "$dir" sync.php
        update_time

        if [[ -f "$dir"/replicator/Replicator.php ]] ; then
            echo $minute $hour 0 0 0 "$php" path/to/run.sh "$dir" Replicator.php
        fi
        update_time
    fi
done

稱呼為generate_crontab.sh 30或類似。請注意,某些目錄在更改後可能會被跳過,因為檔案是按字母順序排序的。

run.sh應該只是

dir=$1
script=$2
if [[ -f "$dir$script" ]] ; then
    cd "$dir"
    /usr/bin/php $script
fi

答案2

最簡單的方法是兩個find指令:

find /var/sync/ -name 'sync.php' -execdir php {} \;
find /var/sync/ -name 'Replicator.php' -execdir php {} \;

這將尋找名為sync.php或 的文件Replicator.php,然後 cd 進入其父目錄並使用 執行它們php。您可以將指令直接加入 crontab 中:

0 30 * * * find /var/sync/ -name 'sync.php' -execdir php {} \;
0 0 * * * find /var/sync/ -name 'Replicator.php' -execdir php {} \;

如果您需要Replicator在腳本之間間隔 30 分鐘運行腳本,您可以執行以下操作:

find /var/sync/ -name 'Replicator.php' -execdir php {} \; -exec sh -c "sleep 30m" \;

這將首先運行該腳本,然後等待 30 分鐘,然後再轉到下一個腳本。

最後,如果您需要確保 i) 永遠不會sync.php同時執行 >1 和 ii) 每個sync腳本在相應腳本完成後運行replicator,您可以使用它作為給 cron 的命令:

find /var/sync/ -name 'Replicator.php' -execdir php {} \; ; find /var/sync/ -name 'sync.php' -execdir php {} \;

上面的程式碼將首先運行每個Replicator.php腳本(一個接著一個,而不是並行),一旦完成,將運行每個sync.php腳本。

答案3

{   cat >    sync.sh  
    ln -s    sync.sh replicator.sh 
    chmod +x sync.sh
} <<"" 
#!/bin/sh
cmd=${0%.*}
cmd=${cmd##*/}
find /var/sync -name "$cmd.php" -exec /usr/bin/php {} \;

因此,當您呼叫 shell 腳本時,shell 將特殊的 shell 參數設定$0為它讀取的腳本的名稱,就像 C 程式設定的那樣argv[0]。您已經完成了一半,因為您正在執行兩個腳本,每個腳本都以其換行目標命名。但是,如果您有兩個名稱,則不需要兩個腳本 - 因此您可以將相同的腳本符號連結到第二個名稱,並$0在呼叫它時進行引用。

因為cron你只需要

i=-30
for  s in sync replicator
do   echo "0 $((i+=30)) * * * /var/sync/$s.sh" | crontab
done

相關內容