Answers:
与其不明智地将ls
输出放入变量中然后对其进行echo
处理(这会删除所有颜色),请使用
ls -a1
从 man ls
-1 list one file per line. Avoid '\n' with -q or -b
我不建议您使用的输出做任何事情ls
,除非显示它:)
例如,使用Shell Glob和for
循环来处理文件...
shopt -s dotglob # to include hidden files*
for i in R/*; do echo "$i"; done
* 虽然这将不包括当前目录.
或其父目录..
echo "$i"
用 printf "%s\n" "$i"
(如果一个文件名以启动一个不会呛“ - ”)。实际上,大多数情况下echo something
应替换为printf "%s\n" "something"
。
R
这里开始,但是总的来说是的。然而,即使是脚本编写,总是尝试容纳-
路径的开头也会带来麻烦:人们认为--
,只有某些命令支持,这表示选项结束。我看过find . [tests] -exec [cmd] -- {} \;
哪里[cmd]
不支持--
。.
无论如何,那里的每条路都开始!但是,这对替换没有参数echo
与printf '%s\n'
。在这里,可以用替换整个循环 printf '%s\n' R/*
。Bash具有printf
内置功能,因此实际上对参数的数量/长度没有限制。
虽然按照@muru的建议将其放在引号中确实可以满足您的要求,但您可能还需要考虑为此使用数组。例如:
IFS=$'\n' dirs=( $(find . -type d) )
的IFS=$'\n'
是告诉bash只分裂上换行符characctersø输出得到的数组的每个元素。没有它,它将在空间上a file name with spaces.txt
分开,因此将是5个单独的元素而不是一个。但是,如果您的文件/目录名称可以包含换行符(\n
),则此方法将无效。它将命令输出的每一行保存为数组的元素。
请注意,我也改变了旧式`command`
到$(command)
这是首选的语法。
现在$dirs
,您有一个名为的数组,每个bof的元素都是上一条命令的输出的一行。例如:
$ find . -type d
.
./olad
./ho
./ha
./ads
./bar
./ga
./da
$ IFS=$'\n' dirs=( $(find . -type d) )
$ for d in "${dirs[@]}"; do
echo "DIR: $d"
done
DIR: .
DIR: ./olad
DIR: ./ho
DIR: ./ha
DIR: ./ads
DIR: ./bar
DIR: ./ga
DIR: ./da
现在,由于某些bash异常(请参阅此处),IFS
执行此操作后需要将其重置为原始值。因此,请保存并重新分配:
oldIFS="$IFS"
IFS=$'\n' dirs=( $(find . -type d) )
IFS="$oldIFS"
或手动执行:
IFS=" "$'\t\n '
或者,只需关闭当前端子。您的新IFS将再次设置原始IFS。
du
OP 的部分对保留输出格式更感兴趣。
mapfile
变得很容易。首先,考虑是否需要存储命令的输出。如果不这样做,只需运行命令。
如果您决定要将命令的输出读取为行的数组,那么的确可以做到这一点的一种方法是禁用globbing,设置IFS
为按行拆分,在(
)
数组创建语法中使用命令替换,然后进行重置IFS
,这比看起来要复杂。Terdon的答案涵盖了某些方法。但我建议您使用mapfile
Bash shell的内置命令来代替以行数组形式读取文本。这是一个从文件读取的简单情况:
mapfile < filename
这会将行读入称为的数组MAPFILE
。如果没有输入重定向 ,您将从外壳程序的标准输入(通常是终端)中读取,而不是从读取文件。要读入默认数组以外的其他数组,请传递其名称。例如,这读入数组:< filename
filename
MAPFILE
lines
mapfile lines < filename
您可能会选择更改的另一种默认行为是,将行尾的换行符保留在原处;它们显示为每个数组元素中的最后一个字符(除非输入以换行符结尾,在这种情况下,最后一个元素没有一个)。要减少这些换行符,使它们不出现在数组中,请将-t
选项传递给mapfile
。例如,这将读取数组records
,并且不会将尾随换行符写入其数组元素:
mapfile -t records < filename
您也可以使用-t
而不传递数组名称;也就是说,它也可以使用隐式数组名MAPFILE
。
所述mapfile
内置支撑件壳的其他选项,并且它可以替代地被援引为readarray
。运行help mapfile
(和help readarray
)以获取详细信息。
但是您不想从文件中读取,而是想从命令的输出中读取。为此,请使用流程替换。此命令读取从命令线some-command
与arguments...
作为其命令行参数并在它们的地方mapfile
的默认阵列MAPFILE
,与其端接换行符移除:
mapfile -t < <(some-command arguments...)
进程替换用实际的文件名代替,可以从中读取运行的输出。该文件是命名管道,而不是常规文件,在Ubuntu上,它的命名方式为,(有时用以外的其他编号表示),但是您无需担心细节,因为shell会处理它所有的幕后。<(some-command arguments...)
some-command arguments...
/dev/fd/63
63
您可能会认为可以通过改用来代替进程替换,但是那样行不通,因为当您有多个由分隔的命令组成的管道时,Bash运行会在subshell中运行所有命令。这样既和自己的运行环境,从初始化,但是从在其中运行的管道外壳的环境中分离出来。在运行的子外壳中,确实填充了该阵列,但是当命令结束时,该阵列将被丢弃。永远不会为调用者创建或修改。some-command arguments... | mapfile -t
|
some-command arguments...
mapfile -t
mapfile -t
MAPFILE
MAPFILE
以下是该示例中terdon的回答的样子,在整体,如果你使用mapfile
:
mapfile -t dirs < <(find . -type d)
for d in "${dirs[@]}"; do
echo "DIR: $d"
done
而已。您无需检查是否IFS
已设置,请跟踪是否已设置以及设置了什么值,将其设置为换行符,然后稍后进行重置或重新设置。您不必禁用通配符(例如,使用set -f
),如果您要认真使用该方法,则确实需要使用通配符,因为文件名可能包含*
和?
,然后[
再重新启用(set +f
)。
您也可以用单个printf
命令完全替换该特定循环-尽管这实际上并不是的好处mapfile
,因为无论您使用mapfile
还是使用其他方法填充数组,都可以这样做。这是一个简短的版本:
mapfile -t < <(find . -type d)
printf 'DIR: %s\n' "${MAPFILE[@]}"
重要的是要记住,正如terdon所提到的,逐行操作并不总是合适的,并且不能与包含换行符的文件名一起正确使用。我建议不要以这种方式命名文件,但是这可能会发生,包括偶然的情况。
您要求“针对所有场景的通用命令”,以及使用mapfile
某种方法达到该目标的方法,但我敦促您重新考虑您的要求。
只需使用一个find
命令即可更好地实现上面显示的任务:
find . -type d -printf 'DIR: %p\n'
您也可以使用外部命令,例如sed
添加DIR:
到每一行的开头。可以说这有点丑陋,并且与find命令不同,它会在包含换行符的文件名中添加额外的“前缀”,但是不管输入内容如何,它都可以工作,因此它可以满足您的要求,并且还是最好将输出读入变量或数组:
find . -type d | sed 's/^/DIR: /'
如果您需要列出并还对找到的每个目录执行操作,例如运行some-command
和传递目录路径作为参数,则也find
可以执行以下操作:
find . -type d -print -exec some-command {} \;
再举一个例子,让我们回到为每行添加前缀的常规任务。假设我想查看的输出,help mapfile
但要对行进行编号。我实际上不会使用mapfile
此方法,也不会使用任何其他方法将其读取到shell变量或shell数组中。假设help mapfile | cat -n
没有给出我想要的格式,我可以使用awk
:
help mapfile | awk '{ printf "%3d: %s\n", NR, $0 }'
将命令的所有输出读入单个变量或数组有时是有用且适当的,但它具有主要缺点。当现有命令或现有命令的组合可能已经满足您的要求或更好时,您不仅要处理使用Shell的额外复杂性,而且命令的整个输出必须存储在内存中。有时您可能知道这不是问题,但有时您可能正在处理大文件。
通常尝试(有时做得正确)的另一种方法是read -r
在循环中逐行读取输入。如果您不需要在以后的行中进行操作时存储前几行,而必须使用长输入,那么它可能会比更好mapfile
。但是,在大多数情况下,只要将其通过管道传递给可以完成工作的命令,也应避免这样做。