如何转到每个目录并执行命令?

发布于 2025-01-20 14:30:46 字数 491 浏览 0 评论 0 原文

我如何编写一个bash脚本,该脚本在parent_directory内部的每个目录和执行 a command

目录结构如下:

parent_directory(名称可以是任何东西 - 不遵循模式)

  • 001(目录名称遵循此模式)
    • 0001.txt(文件名遵循此模式)
    • 0002.TXT
    • 0003.TXT
  • 002
    • 0001.TXT
    • 0002.TXT
    • 0003.TXT
    • 0004.txt
  • 003
    • 0001.TXT

目录的数量未知。

How do I write a bash script that goes through each directory inside a parent_directory and executes a command in each directory.

The directory structure is as follows:

parent_directory (name could be anything - doesnt follow a pattern)

  • 001 (directory names follow this pattern)
    • 0001.txt (filenames follow this pattern)
    • 0002.txt
    • 0003.txt
  • 002
    • 0001.txt
    • 0002.txt
    • 0003.txt
    • 0004.txt
  • 003
    • 0001.txt

the number of directories is unknown.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

£噩梦荏苒 2025-01-27 14:30:46

答案

find . -maxdepth 1 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd" \;

\( ! -name . \) 避免在当前目录中执行命令。

This answer posted by Todd helped me.

find . -maxdepth 1 -type d \( ! -name . \) -exec bash -c "cd '{}' && pwd" \;

The \( ! -name . \) avoids executing the command in current directory.

樱桃奶球 2025-01-27 14:30:46

当当前目录为 parent_directory 时,您可以执行以下操作:

for d in [0-9][0-9][0-9]
do
    ( cd "$d" && your-command-here )
done

创建一个子 shell,因此当前目录不会更改在主脚本中。

You can do the following, when your current directory is parent_directory:

for d in [0-9][0-9][0-9]
do
    ( cd "$d" && your-command-here )
done

The ( and ) create a subshell, so the current directory isn't changed in the main script.

半步萧音过轻尘 2025-01-27 14:30:46

您可以通过管道然后使用 xargs 来实现此目的。问题是您需要使用 -I 标志,它将用每个 xargs 传递的子字符串替换 bash 命令中的子字符串。

ls -d */ | xargs -I {} bash -c "cd '{}' && pwd"

您可能需要将 pwd 替换为您想要在每个目录中执行的任何命令。

You can achieve this by piping and then using xargs. The catch is you need to use the -I flag which will replace the substring in your bash command with the substring passed by each of the xargs.

ls -d */ | xargs -I {} bash -c "cd '{}' && pwd"

You may want to replace pwd with whatever command you want to execute in each directory.

铜锣湾横着走 2025-01-27 14:30:46

如果您使用 GNU find,您可以尝试 -execdir 参数,例如:

find . -type d -execdir realpath "{}" ';'

或(按照 @gniourf_gniourf 评论):

find . -type d -execdir sh -c 'printf "%s/%s\n" "$PWD" "$0"' {} \;

注意:您可以使用 ${0#./} 代替$0 来修复前面的 ./

或者更实际的例子:

find . -name .git -type d -execdir git pull -v ';'

如果你想包含当前目录,使用 - 就更简单了exec

find . -type d -exec sh -c 'cd -P -- "{}" && pwd -P' \;

或使用xargs

find . -type d -print0 | xargs -0 -L1 sh -c 'cd "$0" && pwd && echo Do stuff'

@gniourf_gniourf :

find . -type d -print0 | while IFS= read -r -d '' file; do
# ...
done

上面的例子支持名称中带有空格的目录。


或者通过分配到 bash array:

dirs=($(find . -type d))
for dir in "${dirs[@]}"; do
  cd "$dir"
  echo $PWD
done

. 更改为您的特定文件夹名称。如果不需要递归运行,可以使用:dirs=(*) 代替。上面的示例不支持名称中带有空格的目录。

因此 @gniourf_gniourf 建议,放置输出的唯一正确方法在 Bash 4.4 中,无需使用显式循环即可在数组中查找:

mapfile -t -d '' dirs < <(find . -type d -print0)

或者不是推荐的方式(其中涉及 解析ls):

ls -d */ | awk '{print $NF}' | xargs -n1 sh -c 'cd $0 && pwd && echo Do stuff'

上面的示例将忽略当前目录(按照 OP 的要求),但它会在带有空格的名称上中断。

另请参阅:

If you're using GNU find, you can try -execdir parameter, e.g.:

find . -type d -execdir realpath "{}" ';'

or (as per @gniourf_gniourf comment):

find . -type d -execdir sh -c 'printf "%s/%s\n" "$PWD" "$0"' {} \;

Note: You can use ${0#./} instead of $0 to fix ./ in the front.

or more practical example:

find . -name .git -type d -execdir git pull -v ';'

If you want to include the current directory, it's even simpler by using -exec:

find . -type d -exec sh -c 'cd -P -- "{}" && pwd -P' \;

or using xargs:

find . -type d -print0 | xargs -0 -L1 sh -c 'cd "$0" && pwd && echo Do stuff'

Or similar example suggested by @gniourf_gniourf:

find . -type d -print0 | while IFS= read -r -d '' file; do
# ...
done

The above examples support directories with spaces in their name.


Or by assigning into bash array:

dirs=($(find . -type d))
for dir in "${dirs[@]}"; do
  cd "$dir"
  echo $PWD
done

Change . to your specific folder name. If you don't need to run recursively, you can use: dirs=(*) instead. The above example doesn't support directories with spaces in the name.

So as @gniourf_gniourf suggested, the only proper way to put the output of find in an array without using an explicit loop will be available in Bash 4.4 with:

mapfile -t -d '' dirs < <(find . -type d -print0)

Or not a recommended way (which involves parsing of ls):

ls -d */ | awk '{print $NF}' | xargs -n1 sh -c 'cd $0 && pwd && echo Do stuff'

The above example would ignore the current dir (as requested by OP), but it'll break on names with the spaces.

See also:

云裳 2025-01-27 14:30:46

如果已知Toplevel文件夹,您可以写这样的东西:

for dir in `ls $YOUR_TOP_LEVEL_FOLDER`;
do
    for subdir in `ls $YOUR_TOP_LEVEL_FOLDER/$dir`;
    do
      $(PLAY AS MUCH AS YOU WANT);
    done
done

在$上(随意播放);您可以根据需要放置任意多的代码。

请注意,我没有在任何目录上“ CD”。

干杯,

If the toplevel folder is known you can just write something like this:

for dir in `ls $YOUR_TOP_LEVEL_FOLDER`;
do
    for subdir in `ls $YOUR_TOP_LEVEL_FOLDER/$dir`;
    do
      $(PLAY AS MUCH AS YOU WANT);
    done
done

On the $(PLAY AS MUCH AS YOU WANT); you can put as much code as you want.

Note that I didn't "cd" on any directory.

Cheers,

找个人就嫁了吧 2025-01-27 14:30:46
for dir in PARENT/*
do
  test -d "$dir" || continue
  # Do something with $dir...
done
for dir in PARENT/*
do
  test -d "$dir" || continue
  # Do something with $dir...
done
断爱 2025-01-27 14:30:46

虽然一个衬里适合快速且肮脏的用法,但我更喜欢以下更多的详细版本来编写脚本。这是我使用的模板,它可以处理许多边缘情况,并允许您编写更复杂的代码以在文件夹上执行。您可以在函数dir_command中编写bash代码。在下面,dir_coomand在git中实现标记每个存储库的示例。其余的脚本调用目录中每个文件夹的dir_command。还包括仅通过给定文件夹迭代的示例。

#!/bin/bash

#Use set -x if you want to echo each command while getting executed
#set -x

#Save current directory so we can restore it later
cur=$PWD
#Save command line arguments so functions can access it
args=("$@")

#Put your code in this function
#To access command line arguments use syntax ${args[1]} etc
function dir_command {
    #This example command implements doing git status for folder
    cd $1
    echo "$(tput setaf 2)$1$(tput sgr 0)"
    git tag -a ${args[0]} -m "${args[1]}"
    git push --tags
    cd ..
}

#This loop will go to each immediate child and execute dir_command
find . -maxdepth 1 -type d \( ! -name . \) | while read dir; do
   dir_command "$dir/"
done

#This example loop only loops through give set of folders    
declare -a dirs=("dir1" "dir2" "dir3")
for dir in "${dirs[@]}"; do
    dir_command "$dir/"
done

#Restore the folder
cd "$cur"

While one liners are good for quick and dirty usage, I prefer below more verbose version for writing scripts. This is the template I use which takes care of many edge cases and allows you to write more complex code to execute on a folder. You can write your bash code in the function dir_command. Below, dir_coomand implements tagging each repository in git as an example. Rest of the script calls dir_command for each folder in directory. The example of iterating through only given set of folder is also include.

#!/bin/bash

#Use set -x if you want to echo each command while getting executed
#set -x

#Save current directory so we can restore it later
cur=$PWD
#Save command line arguments so functions can access it
args=("$@")

#Put your code in this function
#To access command line arguments use syntax ${args[1]} etc
function dir_command {
    #This example command implements doing git status for folder
    cd $1
    echo "$(tput setaf 2)$1$(tput sgr 0)"
    git tag -a ${args[0]} -m "${args[1]}"
    git push --tags
    cd ..
}

#This loop will go to each immediate child and execute dir_command
find . -maxdepth 1 -type d \( ! -name . \) | while read dir; do
   dir_command "$dir/"
done

#This example loop only loops through give set of folders    
declare -a dirs=("dir1" "dir2" "dir3")
for dir in "${dirs[@]}"; do
    dir_command "$dir/"
done

#Restore the folder
cd "$cur"
咆哮 2025-01-27 14:30:46

我不明白文件格式的意义,因为您只想遍历文件夹......您在寻找这样的东西吗?

cd parent
find . -type d | while read d; do
   ls $d/
done

I don't get the point with the formating of the file, since you only want to iterate through folders... Are you looking for something like this?

cd parent
find . -type d | while read d; do
   ls $d/
done
故事与诗 2025-01-27 14:30:46

您可以使用

find .

递归搜索当前目录中的所有文件/目录

比您可以像这样通过管道输出 xargs 命令

find . | xargs 'command here'

you can use

find .

to search all files/dirs in the current directory recurive

Than you can pipe the output the xargs command like so

find . | xargs 'command here'
窗影残 2025-01-27 14:30:46

您可以在每个文件夹中以 1 行运行命令序列,例如:

for d in PARENT_FOLDER/*; do (cd "$d" && tar -cvzf $d.tar.gz *.*)); done

You could run sequence of commands in each folder in 1 line like:

for d in PARENT_FOLDER/*; do (cd "$d" && tar -cvzf $d.tar.gz *.*)); done
暮凉 2025-01-27 14:30:46

在以下代码中,您可以使用模式而不是 *过滤不需要的文件。

修改-Mindepth和MaxDepth 1到更高的数字,以便深入研究。 1的值只能达到1级。完全删除这些最小和最大深度论点,以无限深度深处。

注意:仅在您知道文件夹名称中没有空格时才使用以下代码。请参阅在这里,有关详细信息。

#!/bin/bash
for folder in $(find . -mindepth 1 -maxdepth 1 -type d \( -name "*" \) );
do
  cd "$folder" || exit

  echo "Folder: $folder"

  cd -
done

In the below code you are able to use a pattern instead of * to filter unwanted files.

Modify -mindepth and maxdepth 1 to a higher number in order to dive deeper. Value of 1 will go only 1 level deep. Remove these min and max depth arguments completely to go infinitely deep.

Note: Only use the below code when you know there are no spaces in the folder names. See here for details.

#!/bin/bash
for folder in $(find . -mindepth 1 -maxdepth 1 -type d \( -name "*" \) );
do
  cd "$folder" || exit

  echo "Folder: $folder"

  cd -
done
╰◇生如夏花灿烂 2025-01-27 14:30:46
for p in [0-9][0-9][0-9];do
    (
        cd $p
        for f in [0-9][0-9][0-9][0-9]*.txt;do
            ls $f; # Your operands
        done
    )
done
for p in [0-9][0-9][0-9];do
    (
        cd $p
        for f in [0-9][0-9][0-9][0-9]*.txt;do
            ls $f; # Your operands
        done
    )
done
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文