读取每个目录并在 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

相关内容