Parallel 和 ls 带有空格

Parallel 和 ls 带有空格

此命令适用于“常规”名称为 dir1 mydir my-dir 等的目录

ls | parallel 'echo -n {}" "; ls {}|wc -l'

给我每个目录的文件数

但对于带有空格的目录(例如“我的目录”或我的长目录名称)不起作用并给出错误。

如何引用/转义空格?

答案1

GNU Parallel 在处理空格时没有问题:

$ mkdir 'a  b'
$ touch 'a  b/c  d'
$ ls | parallel 'echo -n {}" "; ls {}|wc -l'
a  b 1

-0如果名称包含 \n,则需要:

$ mkdir 'a

b'
$ touch 'a

b/c

d'
# fails
$ ls | parallel 'echo -n {}" "; ls {}|wc -l'
$ parallel 'echo -n {}" "; ls {}|wc -l' ::: *
# works
$ printf "%s\0" * | parallel -0 'echo -n {}" "; ls {}|wc -l'
$ parallel -0 'echo -n {}" "; ls {}|wc -l' ::: *

所以你看到的可能是由于你ls做了一些奇怪的事情:它可能是ls --some-weird-option.尝试\ls改为(或使用printf ... | ... -0-0 ... ::: *如上所示):

\ls | parallel 'echo -n {}" "; ls {}|wc -l'

(PS:你是否知道--tag

parallel --tag 'ls {}|wc -l' ::: *

答案2

parallel没有、xargs、 或来回传递路径名的复杂性:

shopt -s nullglob dotglob

for dir in */; do
    set -- "$dir"/*
    printf '%s:\t%s\n' "${dir%/}" "$#"
done

*也就是说,迭代所有目录并计算每个目录中 glob 扩展的名称数量。

上面是针对 的bash,我们设置nullglobshell 选项来确保不匹配的模式被删除而不是保留未展开的。我还将dotglobshell 选项设置为能够匹配隐藏名称。

shellzsh可以做到这一点,同时过滤仅目录(对于循环)和仅例如常规文件(对于循环体)的全局匹配。在下面的代码中,glob 限定符(ND/)使前面的内容*仅匹配目录,其效果与shell中的nullglob和set 相同,并且以相同的方式使前面的内容仅匹配常规文件。dotglobbash(ND.)*

for dir in *(ND/); do
    set -- $dir/*(ND.)
    printf '%s:\t%s\n' $dir $#
done

你想这样做吗递归地,要获取层次结构中每个目录中的名称计数,那么您可以将上面的内容插入find

find . -type d -exec bash -O nullglob -O dotglob -c '
    for dir do
        set -- "$dir"/*
        printf "%s:\t%s\n" "$dir" "$#"
    done' bash {} +

bash(上面的内容与本答案开头的普通循环有点不同,因为这永远不会计算通过符号链接访问的目录中的名称),或者,

find . -type d -exec zsh -c '
    for dir do
        set -- $dir/*(ND.)
        printf "%s:\t%s\n" $dir $#
    done' zsh {} +

答案3

假设您只想列出执行 cmd 的子目录中的常规文件数量,下面的一行代码可以为您完成此操作:

 $ find . -maxdepth 1 -type d ! -name "." -print0 2>/dev/null \
   | xargs -0 -I {} sh -c 'printf "%20s:  %d\n" "{}" "$(find "{}" -maxdepth 1 -type f 2>/dev/null| wc -l)"'

输出示例:

              ./Maildir:  0
             ./.dvisvgm:  0
               ./.pyenv:  5
             ./.ipython:  0
   ./.ipynb_checkpoints:  3
                ./.tmux:  1
         ./.virtualenvs:  12
         ./seaborn-data:  2
               ./.local:  2
                ./bgpix:  12
                 ./.vim:  7
     ...
  • 2>/dev/null我在每个块中添加find只是为了避免在我用来运行测试的平台上出现一些不需要的文件访问问题。如果您在将文件设置为findcmd的一部分时预见到不会出现此类文件权限问题,则可以取消它。
  • 我还抑制了有关$PWD(您当前的工作目录,表示为.)的所有输出,以符合我上面所述的假设,即您只对计数感兴趣常规的当前一级子目录中的文件。
  • 要计算整个子目录树中的常规文件,从 开始$PWD,只需省略上面-maxdepth 1第一个find块中的全局选项(但将其保留在第二个块中)。

为了更好地突出与依赖的解决方案parallel(如下)的相似性,可以重写上面的内容:

$ xargs -0 -I {} sh -c 'printf "%20s:  %d\n" "{}" "$(find "{}" -maxdepth 1 -type f 2>/dev/null| wc -l)"' \
  < <(find . -maxdepth 1 -type d ! -name "." -print0 2>/dev/null)

如上所示,依靠parallel代替xargs,需要转义一些引号,如下所示(输出与之前完全相同):

$ parallel -0 -I {} \
  'sh -c "printf \"%20s: %d\n\" \"{}\" \"$(find {} -maxdepth 1 -type f 2>/dev/null | wc -l)\""' \
  :::: < <(find -maxdepth 1 -type d ! -name "." -print0 2>/dev/null)
  • xargsparallel使用相同的两个参数-0 -I {}

  • 'sh -c "printf ..."'要执行的 shell 命令是单引号、和之间的 Bourne shell
  • 由 引入的并行化输入::::是进程替换输入文件,<(...)其中包含“查找从 cmd 开始的第一个子级别的所有子目录”的输出$PWD

答案4

使用 find 而不是 ls 找到了解决方法。

find * -type d -maxdepth 0 | parallel 'echo -n {}" "; ls {}|wc -l'

相关内容