我想循环遍历一个目录,对于每个不是目录的文件,我想将名称附加在一起并用逗号分隔。
files=""
for f in $(ls ~/Downloads | grep -v ^d)
do
files+="$f"","
done
echo "$files"
此处的下载文件夹包含如下文件
这个:01 - Agar Mujhse Mohabbat Hai - www.downloadming.com.mp3
预期输出:01 - 祝福你一切顺利 - www.downloadming.com.mp3
实际产量:01,-,Agar,Mujhse,Mohabbat,Hai,-,www.downloadming.com.mp3
欢迎所有解决方案。
答案1
files=()
cd ~/Downloads
for file in *; do
[[ -d $file ]] && continue
files+=( "$file" )
done
printf "%s\n" "${files[@]}"
comma_separated=$(IFS=,; echo "${files[*]}")
echo "$comma_separated"
正如你所见,for f in $(ls)
迭代字在输出中,而不是线。ls
当您想让人类的眼睛看到文件时,请使用 shell 文件名模式,当您想让脚本对文件名进行某些操作时,请使用 shell 文件名模式。
答案2
更短的解决方案:
find ~/Downloads -type f -not -name "d*" -printf "%f,"
答案3
使用while read
而不是for
我发现迭代文件名列表的最安全的方法是通过管道传输while read
:
find ~/Downloads -type f | while read filename; do
echo "found file called $filename";
done
如您所见,它将整个文件名(以行分隔而不是空格分隔)放入您提供的变量名中,此处$filename
。
然而,从最初的问题来看,如果真正想要的是一个以逗号分隔的文件列表,那么你甚至不需要迭代,只需tr
使用tr将行分隔符转换为逗号:
find ~/Downloads -type f | tr '\n' ','
答案4
这是内部字段分隔符的问题。相反,这不是问题,它就是这样设计的。它用空格分隔输入,就像在位置参数中一样。
来自 Bash 手册页:
为了 姓名[ [在[单词... ] ] ; ]做 列表;完毕
下面的单词列表在展开,生成项目列表。变量姓名依次设置为此列表的每个元素,并且列表每次都会执行。如果 在 单词 被省略,为了命令执行列表对于设置的每个位置参数,执行一次。返回状态是执行的最后一个命令的退出状态。如果以下项目的扩展在导致列表为空,不执行任何命令,返回状态为 0。
这意味着,它将表达式的结果扩展为一个单词列表。这就是内部字段分隔符 $IFS 的作用所在。默认情况下,$IFS 设置为空格(空格、制表符和换行符)。这意味着一个字段由直到(但不包括)这些空格字符的所有内容组成。由于文件名由空格组成,因此它将它们视为几个不同的字段并为每个字段运行一次循环。解决此问题的方法是调整 $IFS 值。
因此,您需要将 $IFS 调整为空格以外的其他内容。但是,使用当前方法,除非它们恰好都有文件扩展名,否则无法分辨一个文件名在哪里结束,另一个文件名在哪里开始。更简单的方法是使用find
如下实用程序:
#Save the original IFS
oldifs=$IFS
#Set the new IFS to the ASCII null character \x00
IFS=$(echo -ne "\x00")
files="" #To prevent scoping issues
#Use the find utility to find and print just regular files (not directories),
#and not going further than ~/Downloads (remove '-maxlength 1' to make the
#search fully recursive), then print the filenames in the format
#file1\x00file2\x00file3\x00file4...
#However, since our new IFS is now \x00, the boundary upon which these filenames are
#separated now works as expected
for f in $(find ~/Downloads -maxdepth 1 -type f -print0)
do
files="$files,$f"
done
echo "$files"
#Reset IFS
IFS=$oldifs
我还想指出,您的语句ls ~/Downloads | grep -v ^d
可能没有达到您的预期。听起来您试图过滤掉目录,但实际上发生的是它过滤掉了以“d”开头的文件。这是因为您没有使用长格式ls
like ls -l
。ls -l
打印输出如下:
drwxrwxrw- user group SomeDirectory size date_modified filename
而 'ls' 仅打印文件名称:
SomeDirectory
file1
file2
file3