【问题标题】:How do you send the output of ls to mv?你如何将ls的输出发送到mv?
【发布时间】:2010-10-30 13:52:39
【问题描述】:

我知道你可以用find 来做到这一点,但是有没有办法在unix 命令行中将ls 的输出发送到mv

【问题讨论】:

  • 您可以将几乎任何命令的输出发送到几乎任何其他带有竖线 ('|') 字符的命令......但我认为这不是您真正想要做的。你能解释一下你想完成什么吗?
  • 如果您使用 find 命令描述您的问题会有所帮助。
  • find + xargs 可能是做你想做的最好的方式。为什么要坚持使用 ls + mv?
  • @JesperE:find + xargs 很傻。 find 有一个 -exec 选项,它更可靠,不需要复杂的管道,甚至不需要 shell 逻辑。

标签: bash command-line


【解决方案1】:

ls 是一个工具,用于显示有关目录中文件名的一些统计信息。

不是您应该用来枚举它们并将它们传递给另一个工具以在那里使用它的工具。解析ls几乎总是是错误的,而且在很多方面都存在错误。

关于解析 ls 的坏处的详细文档,你应该真的阅读,查看:http://mywiki.wooledge.org/ParsingLs

相反,您应该使用 glob 或 find,具体取决于您想要实现的目标:

mv * /foo
find . -exec mv {} /foo \;

解析ls 的主要错误来源是 ls 将所有文件名转储到单个输出字符串中,除此之外无法区分文件名。如您所知,整个ls 输出可能是一个文件名!

解析ls 的第二个不良来源来自半个世界使用 bash 的错误方式。他们认为for 在执行以下操作时会神奇地执行他们希望它执行的操作:

for file in `ls`  # Never do this!
for file in $(ls) # Exactly the same thing.

for 是一个迭代参数的 bash 内置函数。并且$(ls) 获取ls 的输出并将其拆分为参数,只要有spacesnewlinestabs。这基本上意味着,您正在迭代单词,而不是文件名。更糟糕的是,您要求 bask 获取每个残缺的文件名单词,然后将它们视为可能与当前目录中的文件名匹配的 glob。因此,如果您的文件名包含一个单词,该单词恰好是与当前目录中的其他文件名匹配的 glob,则该单词将消失,并且所有匹配的文件名都将出现!

mv `ls` /foo      # Exact same badness as the ''for'' thing.

【讨论】:

  • find . -exec mv {} /foo \;{} 在这里是什么意思?有没有我们在{}里面放东西的时候?
  • 在我的机器上,man find | grep {} 表示:命令参数 {}(大括号)被当前路径名替换。
  • @ThangPham {} 是 find 放置当前文件名的位置。您永远不会在 {} 中添加任何内容,并且它应该始终作为一个参数单独存在(即,永远不要“在”像“{}.txt”这样的参数“内部”。
【解决方案2】:

一种方法是使用反引号:

mv `ls *.boo` subdir

编辑:但是,这是脆弱并且推荐的——请参阅@lhunath 的回答以获得详细的解释和建议。

【讨论】:

  • 与更简单的 "mv .boo subdir" *except 完全相同 您的将不会处理名称中带有空格的文件,并且如果您有“别名 ls='ls --color=always'”,它将无法工作。你不应该以这种方式依赖 ls 的输出。
  • 总是可以直接调用ls,即whereis ls,然后是'/bin/ls'或者返回什么。
  • 这仍然不能解决文件名中的空格问题。
  • @Dennis,好点子——最好不要让文件名带有空格,但除非你能保证是这种情况,否则反引号、xargs(没有 -0 技巧)等都会中断。
  • 我确信 -- 现在删除这个答案!
【解决方案3】:

到目前为止,对于带有空格的文件名,没有一个答案是安全的。试试这个:

for i in *; do mv "$i" some_dir/; done

您当然可以使用任何您喜欢的 glob 模式来代替 *

【讨论】:

    【解决方案4】:

    不完全确定您要在这里实现什么,但这是一种可能性:

    “xargs”部分是重要的部分,其他一切都只是设置。这样做的效果是获取“ls”输出的所有内容并为其添加“.txt”扩展名。

    $ mkdir xxx # $ cd xxx $ touch a b c x y z $ ls a b c x y z $ ls | xargs -Ifile mv 文件 file.txt $ ls a.txt b.txt c.txt x.txt y.txt z.txt $

    类似的事情也可以通过以下方式实现:

    $ touch a b c x y z $ for i in `ls`;do mv $i ${i}.txt;完毕 $ ls a.txt b.txt c.txt x.txt y.txt z.txt $

    我更喜欢第二种方式。如果不阅读手册页或转到我的“可爱技巧”文件,我永远记不起 xargs 是如何工作的。

    希望这会有所帮助。

    【讨论】:

    • @kungfugraig:请注意,@David Williamson 关于包含空格的文件名的评论也适用于您的 xargs 使用。
    • 没有特别需要 ls - "for i in *; do anything; done" 也可以。
    【解决方案5】:

    查看find -exec {},因为它可能比ls 更好,但这取决于您要达到的目标。

    【讨论】:

    • 你应该举个例子,比如“find . -name '*.txt' -exec cp '{}' /stuff ';'”。特别是因为引用正确并不明显。
    【解决方案6】:

    您不应该将 ls 的输出用作另一个命令的输入。名称中包含空格的文件很困难,如果您有 ANSI 转义序列,则包含以下内容:

    alias ls-'ls --color=always'
    

    例如。

    始终使用 find 或 xargs(带 -0)或通配符。

    另外,您没有说是要移动文件还是重命名文件。每个都会有不同的处理方式。

    编辑:将 -0 添加到 xargs(感谢提醒)

    【讨论】:

    • xargs 在名称中也存在空格问题(除非它是由 find 的 -print0 提供的,并使用 -0 本身运行)。
    • ...它总是应该在其中存在路径中有空格的风险
    • 此外,如果不使用 -0 选项运行,xargs 还会尝试吃掉文件名中包含的引号字符。我建议您从响应中删除 xargs 或添加 -0 是绝对必须的。
    【解决方案7】:

    正如其他人所建议的那样,反引号效果很好。也见 xargs。对于非常复杂的东西,将其通过管道传输到 sed,创建所需的命令列表,然后将 sed 的输出通过管道传输到 sh 再次运行。

    这是一个 find 的例子,但它也适用于 ls:

    http://github.com/DonBranson/scripts/blob/f09d24629ab6eb3ce509d4d3078818430306b063/jarfinder.sh

    【讨论】:

    • 投反对票?严重地? :( 我承认其他很好的答案,并提供了一些在反引号不起作用的情况下有效的其他方法。
    【解决方案8】:
    /bin/ls | tr '\n' '\0' | xargs -0 -i% mv % /path/to/destdir/
    

    “无用的 ls”,但应该可以。通过指定 ls(1) 的完整路径,您可以避免与前面一些帖子中提到的 ls(1) 别名冲突。 tr(1) 命令与“xargs -0”一起使该命令适用于包含(ugh)空格的文件名。它不适用于包含换行符的文件名,但在文件系统中有这样的文件名是自找麻烦,所以它可能不会是一个大问题。但是可能存在带有换行符的文件名,因此更好的解决方案是使用“find -print0”:

    find /path/to/srcdir -type f -print0 | xargs -0 -i% mv % dest/
    

    【讨论】:

    • 文件名可以包含换行符。
    【解决方案9】:

    只需使用find 或你的shell globing!

    find . -depth=1 -exec mv {} /tmp/blah/ \;
    

    ..或..

    mv * /tmp/blah/
    

    您不必担心 ls 输出中的颜色或其他管道异常 - Linux 基本上允许文件名中包含除空字节以外的任何字符。例如:

    $ touch "blah\new|
    > "
    $ ls | xargs file
    blahnew|:                  cannot open `blahnew|' (No such file or directory)
    

    ..但是 find 完美工作:

    $ find . -exec file {} \;
    ./blah\new|
    : empty
    

    【讨论】:

      【解决方案10】:

      对于更复杂的情况(通常在脚本中),使用 bash 数组来构建参数列表可能非常有用。可以创建一个数组并使用适当的条件逻辑向其推送。例如:

      跟进 @lhunath 的 recommendation 以使用 glob 或查找,将匹配 glob 模式的文件推送到数组:

      myargs=()
      # don't push if the glob does not match anything
      shopt -s nullglob
      myargs+=(myfiles*)
      

      将匹配到的文件推送到数组:https://stackoverflow.com/a/23357277/430128

      根据需要添加其他参数:

      myargs+=("Some directory")
      

      在调用类似mv 的命令时使用myargs

      mv "${myargs[@]}"
      

      注意数组myargs 的引用以正确传递带有空格的数组元素。

      【讨论】:

        【解决方案11】:

        你把 ls 用反引号括起来,放在 mv 之后,所以像这样......

        mv `ls` somewhere/
        

        但请记住,如果您的任何文件名中包含空格,则效果不佳。

        另外,做这样的事情会更简单:mv filepattern* somewhere/

        【讨论】:

          【解决方案12】:
          #!/bin/bash
          
          for i in $( ls * );
          do
          mv $1 /backup/$1
          done
          

          否则,这是 sybreon 的 find 解决方案,而不是绿色 mv ls 解决方案。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2015-05-22
            • 2013-03-08
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多