【问题标题】:Print list of files in a directory to a text file (but not the text file itself) from terminal从终端将目录中的文件列表打印到文本文件(但不是文本文件本身)
【发布时间】:2017-04-30 17:04:23
【问题描述】:

我想将目录中每个文件的所有文件名打印到.txt 文件中。

假设我有一个包含 3 个文件的目录:

file1.txt
file2.txt
file3.txt

我尝试使用ls > output.txt

问题是,当我打开output.txt 时,我找到了这个列表:

file1.txt
file2.txt
file3.txt
output.txt

有没有办法避免打印我重定向输出的文件名?或者更好的是有一个命令能够打印目录中除一个之外的所有文件的文件名吗?

【问题讨论】:

  • 是否有理由将输出打印在与其他文件相同的目录中?
  • @Jeremy 使用它对我来说更容易。但是,是的,我可以在另一个目录中创建文件,完全避免了这个问题。没想到

标签: bash ls


【解决方案1】:

一般来说,解析ls的输出并不好,尤其是在编写需要长期保持良好状态的生产质量脚本时。请参阅此页面以了解原因:Don't parse ls output

在您的示例中,output.txtls > output.txt 中输出的一部分,因为 shell 在运行 ls 之前安排了重定向(到 output.txt)。

为您的案例获得正确行为的最简单方法是:

ls file*txt > output.txt # as long as you are looking for files named that way

或者,将输出存储在隐藏文件中(或其他目录中的普通文件中),然后将其移动到最终位置:

ls > .output.txt && mv .output.txt output.txt

更通用的解决方案是使用grep -v

ls | grep -vFx output.txt > output.txt

或者,您可以使用数组:

files=( "$(ls)" )
printf '%s\n' "${files[@]}" > output.txt

【讨论】:

  • 我认为这仍然与多行文件名发生冲突,其中一行是output.txt(诚然是一个奇特的案例)。至于另一个解决方案:为什么不只使用字符串变量而不是单元素数组? files="($ls)"; printf '%s' "$files" > output.txt
【解决方案2】:
printf '%s\n' * > output.txt

请注意,这假设没有预先存在的 output.txt 文件 - 如果是,请先将其删除。

  • printf '%s\n' * 使用globbing (filename expansion) 健壮地逐行打印位于当前目录中的所有文件和子目录的名称。

  • Globbing 发生 before output.txt 是通过输出重定向 > output.txt 创建的(这仍然发生在命令 执行 之前,这解释了您的问题),所以它的名字包含在输出中。

  • 通配符还避免使用ls,它在脚本中的使用是generally discouraged

【讨论】:

    【解决方案3】:

    ls 有一个忽略选项,我们也可以使用find 命令。

    • 使用带有忽略选项的ls

      ls -I "output.txt" > output.txt
      ls --ignore "output.txt" > output.txt
      

    -I, --ignore 相同。这个选项说,就像在手册页中一样,不要列出与 shell PATTERN 匹配的隐含条目。

    • 使用find

      find \! -name "output.txt" > output.txt
      

    find 中的-name 选项查找名称与模式匹配的文件/目录。 ! -name 排除名称与模式匹配的人。

        find \! -name "output.txt" -printf '%P\n' > output.txt
    

    %P 去掉路径,只给出名字。

    【讨论】:

    • 您的命令假定使用 GNU 实用程序 (Linux) - 请在您的回答中明确该先决条件。要使 find 命令等效,您 (a) 需要 -maxdepth 1 以防止递归,并且 (b) 您必须对文件名列表进行排序。
    • 我的机器上没有忽略选项。我没有使用 Linux
    【解决方案4】:

    不假设文件名的最安全方法是使用 bash 数组(在内存中)或临时文件。临时文件不需要内存,因此它可能更安全。比如:

    #!/bin/bash
    tmp=$(tempfile)
    ls > $tmp
    mv $tmp output.txt
    

    【讨论】:

      【解决方案5】:

      使用lsawk 命令可以获得正确的输出。

      ls -ltr | awk '/txt/ {print $9}' > output.txt
      

      这将只打印文件名。

      【讨论】:

        【解决方案6】:

        我的方式是这样的:

        ls *.txt > output.txt
        

        请注意,shell 在运行之前总是会扩展所有 glob。在您的具体情况下,全局扩展过程如下:

        # "ls *.txt > output.txt" will be expanded as
        ls file1.txt file2.txt file3.txt > output.txt
        

        您在最终输出文件中获得“output.txt”的原因是重定向实际上在所有连接的程序中都有效 同时强>.

        这意味着重定向过程不会在程序ls 结束时发生,而是在每次ls 产生一行输出时发生。在您的情况下,当ls 完成产生第一行时,将创建文件“output.txt”,最终将由ls 返回。

        【讨论】:

          猜你喜欢
          • 2011-03-03
          • 2018-02-22
          • 1970-01-01
          • 2014-09-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-01
          • 1970-01-01
          相关资源
          最近更新 更多