【问题标题】:How do I make an array of specific files in a directory using the command shell in linux?如何使用 linux 中的命令 shell 在目录中创建一组特定文件?
【发布时间】:2016-09-11 01:09:40
【问题描述】:

我一直在尝试将目录中小于 2 兆字节的文件添加到数组中,然后将这些文件添加到 git 中,然后提交。

  find . -type f -size -2M
  i=0
  while read line
  do
    array[ $i ]="$line"
    (( i++ ))
  done
  for item in "${array[@]}"
  do
    git add "$item"
  done
  git commit -am "${COMMIT_MESSAGE}"

该脚本应该添加和提交 2M 以下的文件。但我的脚本正在添加和提交所有文件(包括大于 2M 的文件)。 我做错了什么?

【问题讨论】:

  • 请将您的代码粘贴到您的问题中,而不是作为图像。这样你更有可能得到答案。
  • 试试git add $(find . -type f -size -2M); git commit -m "Commit message"
  • 是什么将您的find 连接到while read line 循环?当然你想要find .... | while read line ... 但是上面的评论看起来非常有用。祝你好运。
  • @bobsterman 这有帮助。非常感谢。
  • 最大的问题是“哪个shell”?并非所有都支持数组(POSIX、Bourne 等)

标签: linux git bash shell


【解决方案1】:

简短形式:解决眼前的问题

首先,关于从find正确读取文件,请遵循@chepner's answer中的建议。

其次,关于为什么你现有的代码包含所有在过去的版本中已经添加到 git 的文件,而不是只包含小于 2MB 的文件,尽管没有运行git add:那是因为你'将-a 参数传递给git commit

find . -type f -size -2M -exec git add -- {} +
git commit -m "Commit message" # no -a here!

长格式:解释问题

要清楚我所说的“尽管没有运行git add根本”是什么意思 - 正如目前所写的那样,find 的输出实际上并没有传递到 @ 987654331@循环。

find .
while read ...

不会将find 的输出重定向到while read 循环。因此,您的 while read line 循环仅在标准输入上为您的脚本提供的输入(如果有)进行迭代(并且find 的输出被写入标准输出)。

有关其他问题,请参阅下面的注释。


长格式:按要求回答问题

现在,回答你的文字问题,关于如何构建一个 shell 数组,正确的做法如下所示:

# correctly building a shell array
files=( )
while IFS= read -r -d '' file; do
  files+=( "$file" )
done < <(find . -type f -size -2M -print0)

## using that array efficiently (if not huge)
#git add -- "${files[@]}"

# using that array efficiently (if potentially huge)
printf '%s\0' "${files[@]}" | xargs -0 git add --

实施说明:

  • while 循环放入外壳可确保在完成运行后可以访问对它所做的变量状态的更改。使用&lt; &lt;(...) 成语是必要的,如BashFAQ #24 中所述。
  • 将多个文件传递给单个 git add 实例比为每个文件调用一次 git add 高效得多。
  • find 使用-print0 参数会导致find 的输出中的文件名由NUL 分隔(与换行文字不同,文件名中不可能存在NUL)。
  • 使用IFS= read -r -d '' file 成语(如BashFAQ #1 中所述)以逐字节文字形式从find 读取名称。
  • 使用array+=( "$value" ) 比保留一个整数索引计数器并在每次追加之间递增它要容易得多。

【讨论】:

  • 对于那些生活在未来的人(Bash 4.4 即将发布):mapfile -t -d '' files &lt; &lt;(find . -type f -size -2M -print0)。通常需要注意的是 -print0 不是标准的……
【解决方案2】:

使用find 运行git add

find . -type f -size -2M -exec git add {} +

这适用于所有个有效的文件名。

commit-a 选项可能不是必需的。如果您只有未跟踪的大文件,那么commit -a 无论如何都不会添加它们,并且您已经添加了所有修改后的小文件。但是,如果您有大型跟踪文件,并且您不想将其包含在待处理的提交中,那么您确实需要删除它。

【讨论】:

  • 假设我“暗示”了它;让我听起来不那么粗心:)
  • (但最初在错误的位置添加-exec 确实让我粗心,唉。)
  • -a 仅在您不想提交已修改但已跟踪的大文件时才会出现问题。这里的假设似乎是使用find 代替git add * 以避免将大型未跟踪文件添加到repo。
  • OP 非常明确地指出,他们引发问题的直接问题是所有文件都已提交。如果-a 没有问题,你能看到他们之前的代码是如何做到这一点的吗?
  • "add and commit" 意味着它们不在存储库中,这意味着commit -a 会忽略它们。
【解决方案3】:

请尝试

find . -type f -size -2M -print0 | xargs -0 git add
git commit -m 'Message here'

【讨论】:

  • xargs 不应与find 一起使用,除非您使用find-print0xargs-0 开关(无论如何都不是标准的)。使用 find,使用 -exec,如 chepner 的回答。
  • 错过了直到今天才被编辑。现在好多了。
【解决方案4】:

第一条评论的单行命令就好 但如果您仍然需要脚本来添加一些额外的逻辑,请尝试使用:

#!/bin/bash

root_dir="/home/myuser/myproject"
exclude_path="dirname_to_exclude"
max_size="-2048k"

for file in $(find $root_dir -type f -size $max_size | grep -v $exclude_path)
do
    git add $file
done

git commit -m "blablabla..."

【讨论】:

  • for file in $(find ...) 是(可悲的是!)一个非常常见的反模式:它被包含空格或全局字符的文件名破坏(并且它产生了一个无用的子shell)。有关使用 find 的正确方法,请参阅 chepner 的回答。
  • 另外,git add $file 本身已损坏 - 看看它如何处理名称中包含空格或 glob 的文件(例如,如果 file='*** READ ME FIRST ***.txt')。需要git add "$file" 以防止字符串拆分或全局扩展,同样适用于所有其他未引用的全局。 (不带引号的扩展可能会中断更多的空格——例如,如果您的脚本中的其他代码设置为IFS=/,那么不带引号的扩展将使每个目录元素都成为自己的单词)..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-11-09
  • 2020-08-17
  • 1970-01-01
  • 1970-01-01
  • 2021-01-28
  • 2013-04-15
  • 2012-04-26
相关资源
最近更新 更多