如何过滤脚本的参数并将其保存到新文件?

如何过滤脚本的参数并将其保存到新文件?

我正在编写一个 bash 脚本或命令,它将:

  • 保存到文件X或来自其参数的文件,这些参数是包含不超过 20 个文件的目录。
  • 文件的名称,以 表示X,必须从键盘输入。
  • 该文件X应包含目录名称及其权限。

我首先通过以下方式查找文件数量不超过 20 个的目录:

find . -maxdepth 1 -type d -exec bash -c "echo -ne '{} '; ls '{}' | wc -l" \; | awk '$NF<=20'

但是我在将参数保存到特定文件时遇到了麻烦。

答案1

将输出写入由用户交互提供名称的文件中

您曾说过,您在保存到脚本用户提供名称的特定文件时遇到了麻烦,并且您强调了想要特别帮助解决此问题。但您没有提到提示用户输入文件名时遇到任何问题。因此,我假设您已经解决了这个问题,并且您正在执行类似 的操作,其中是将保存用户提供的输出文件名的变量的名称。IFS= read -r outfileoutfile

outfile如果您只剩下将输出写入保存名称的文件,那么您已经非常接近目标了。outfile用您选择的任何变量名替换...

  • 与其他shell 中的参数,你读入的变量可以扩展并且$outfile你可以也应该防止不受欢迎的分裂通配符将其包含在双引号,即通过书写"$outfile"
  • 与其他文件名一样,您可以使用输出重定向写入此文件。因此,就像您可以使用>out.txt写入名称为 的文件一样,您可以通过写入out.txt写入名称保存在您的变量中的文件。outfile>"$outfile"

这可能就是您所需要的。但您可能还需要考虑其他一些修改。

您可以使用find并执行最多您 所需 所需 所需-exec.

作为steeldriver 说,从你陈述的目标来看,你似乎不希望你的脚本考虑全部当前目录中的文件,但只考虑传递给脚本的参数。find . -maxdepth 1 ...

尽管如此,这样做还是很合理的find,这样做的一个好处是如果你确实需要稍后修改它以使其以递归方式运行,这将非常容易。(另一方面,为准备你正在开发的功能而付出额外的努力通常是不明智的。可能需要稍后添加,因为很难预测这些功能实际上会是什么。

在本回答的其余部分,我将探讨这种可能性。我将给出可能是一个完整的解决方案,但您可能会或可能不会决定它是否适合您的需求。您可能希望以某种方式修改它。我的目标不是提供完善的脚本,而是演示您可以使用的技术。(您可能会得到其他答案,这些答案演示了诸如 shell 循环和使用 as 检查每个文件之类的方法[ -d filename ]steeldriver 建议

如果你想使用find,您可以将脚本收到的参数传递给find作为其搜索的根,以代替您当前写入的位置.,并使用-maxdepth 0,这样它就不会真正深入其中。这如果脚本中的某些文件名参数以 开头,则会发生错误-,因为它们可能会被特殊解释find(作为选项、谓词或操作)。但大多数命令都会对带有前导-字符的参数进行特殊处理;通常,为其他用户记录此行为并确保您不会意外将此类路径传递给脚本就足够了。(如果您确实想考虑当前目录中名为 的条目-foo,则可以将其名称传递为./-foo。)

为简单起见,我将首先考虑如何解决更简单的问题,即输出传递给脚本的所有目录名称参数的行,无论它们包含多少个条目(即忽略“不超过 20 个文件”的要求)。

find "$@" -maxdepth 0 -type d -printf '%M %p\n' 2>/dev/null >"$outfile"

工作原理如下:

  • "$@"扩展为脚本所有位置参数的列表。(传递给脚本的命令行参数将成为其位置参数$1$2$3等的值。)每个参数都将find作为单独的参数传递。即使某些名称包含空格,此方法也能奏效。
  • -maxdepth 0告诉find不要查看比您明确传递给它的路径更深的内容。因此它只会考虑传递给脚本的命令行参数。
  • -type d仅查找目录,就像您在命令中使用它一样。
  • -printf '%M %p\n'打印一个ls-style 符号权限字符串 ( %M)、一个空格、文件名 ( %p) 并结束行 ( )。请参阅操作\n的文档-printfman find
  • 根据您的描述,我不确定您的脚本是否应该认为用户传递的参数根本不标识任何类型的文件、目录或其他内容是正常的。因此,我没有提出可能不适用于您的要求,而是采用了快速而肮脏的方法,即隐藏来自 的所有错误消息find2>/dev/null这会重定向标准误差文件描述符2)/dev/null(一种特殊的“装置”,扔掉它的输入)。如果您想在传递给脚本的参数根本没有命名任何内容时看到错误消息,只需省略此部分。(即使显示了错误消息,脚本仍将执行其工作。因此它们实际上是警告而不是错误。)如果您想要其他行为,您可以对此进行处理和/或提供有关您的要求的更多信息。我称此为“快速而肮脏”的方法,因为可以find发出其他错误和警告消息,并且它们也会被抑制。
  • >"$outfile"写入名称保存在outfile变量中的输出文件,如上所述。

需要考虑的一件事是,如果用户没有通过任何参数。然后find看不到任何根。当发生这种情况时,它的行为就像您传递了一样.。(并非所有find实现都将根的缺失视为隐含的.,但 GNU find,Ubuntu 中的 find 实现,会这样做。)您应该处理这个问题...如果您不想要这种行为。

过滤包含超过 20 个文件的目录

调用另一个命令来计算每个目录中的文件数量是合理的-exec,你的方法是正确的。但这应该操作-printf(或您用来导致find输出文件名的任何其他操作),以便此操作仅发生在通过测试的目录中。

如果你选择通过计算输出行数来计算文件数量ls命令产生的结果,ls则应使用选项调用该命令-q。这是因为文件名可能包含您可能意想不到的奇怪字符,包括新队人物.-q用 s 替换这些字符?,这在很多情况下是不够的,但由于你实际上并没有看着名字,没问题。(您可能还想使用该-1选项,因为尽管这是多余的ls输出到管道(如此处所示),它清楚地记录了您为ls每个文件发出一行的意图。)一般来说,您应该避免解析ls,但由于您不关心实际的文件名并-q确保每个文件一行,所以没问题。

但是我将展示一种不同的技术。你可以将此测试放在之后-type d但之前-printf

-exec bash -c 'a=("$1"/*); ((${#a[@]} <= 20))' _ {} \;

bash -c ...使用bashshell。我写成了bash而不是因为我想使用sh一个功能:bash数组。shell 运行a=("$1"/*); ((${#a[@]} <= 20))。在第一个命令中a=("$1"/*)

  • "$1"扩展为第一个位置参数,它对应于代替的文件名find传递{}。这是正在处理的目录的名称。
  • 写作"$1"/* 扩展到该目录中包含的文件的路径,省略名称以 开头的文件.,如ls命令所示。(如果您也想计算这些文件,可以shopt -s dotglob;在 之前写入。)a=...
  • 在变量赋值的右侧用括号括起来导致它被存储在一个数组变量中

数组被a称为其大小稍后可获得通过编写${#a[@]}。第二条命令((${#a[@]} <= 20))利用了以下优势:

  • (( )) 计算算术表达式如果它们非零,则返回 true/成功,如果它们为零,则返回 false/失败。
  • 如上所述,${#a[@]}扩展为数组的大小,即find代替传递的目录中的条目数{}。这是您需要与 20 进行比较的内容。
  • <= 20将其与 20 进行比较。

您可以尝试使用 20 以外的其他值来确保其正常工作。

综合起来

完整的脚本看起来像这样(但请记住,我不知道您的所有要求,这更多的是为了演示而不是解决方案):

#!/bin/sh

if [ "$#" -eq 0 ]; then
    printf '%s: warning: no arguments, behaving as "%s ."\n' "$0" "$0"
fi

printf 'Output filename> '
IFS= read -r outfile

find "$@" -maxdepth 0 -type d \
         -exec bash -c 'a=("$1"/*); ((${#a[@]} <= 20))' _ {} \; \
         -printf '%M %p\n' 2>/dev/null >"$outfile"

一些注意事项:

  • 运行脚本的用户必须小心输入的输出文件名。如果该文件存在,它将被覆盖。
  • 我继续演示了一种处理没有参数的情况的可能方法:继续,但警告用户,就好像他们运行了脚本并通过了一样.
  • 尽管从 运行的 shell 命令的find操作-exec依赖bash于数组,但读取用户输入并调用的较大脚本find 没有使用任何此类功能。您可以根据bash需要使用 来运行它,并且可以将 hashbang 更改为#!/bin/bash。但我已将其写为#!/bin/sh,因为这样就足够了。

我将脚本文件命名为lt20。您可以随意命名它,但您应该通过运行使其可执行chmod +x lt20(但使用您为其指定的任何名称)。以下是使用该脚本的样子,其中用户输入的文本以斜体显示:

$ ./lt20 /usr/*
Output filename> out
$ cat out
drwxr-xr-x /usr/arm-linux-gnueabi
drwxr-xr-x /usr/arm-linux-gnueabihf
drwxr-xr-x /usr/games
drwxr-xr-x /usr/lib32
drwxr-xr-x /usr/libexec
drwxr-xr-x /usr/local
drwxr-xr-x /usr/src

我的机器有 12 个 子目录/usr,它们都作为参数传递给脚本,但只.列出了条目数不超过 20 个的子目录(除以 开头的条目外)。它们碰巧都具有相同的权限,drwxr-xr-x但脚本仅在它们实际上具有相同权限时才为每个条目显示相同的权限字符串。

相关内容