我想删除包含的文件size = 0
。因此我尝试:
find ./ -size 0 | xargs rm
但是,对于名称以空格开头的文件,它存在问题。
在互联网上搜索我发现了这个:
find ./ -size 0 -exec rm -i {} \;
可以。但是,我认为我的方法xargs
太复杂了。
这是什么{} \;
意思?
有人可以解释一下吗?
我的英语不是很好,所以请使用简单的书写。
答案1
{}
对 来说完全没有任何意义bash
,因此不加修改地作为参数传递给这里执行的命令find
。
另一方面,;
对 有特定含义bash
。它通常用于分隔同一命令行上的连续命令。此处的反斜杠\;
恰好用于防止分号被 解释为命令分隔符,bash
然后允许它作为参数传递给底层命令find
。引用分号,即";"
或';'
,可能是使其保持未处理的另一种方法。
命令:
find ./ -size 0 -exec rm -i {} \;
意思是:在当前目录中查找(请注意,/
这里的 是无用的,因为.
它无论如何只能是一个目录)任何大小为 0 的对象,对于找到的每个对象,运行命令rm -i name
,即以交互方式提示每个文件是否要删除它。{}
替换为执行命令中找到的每个文件名。 一个很好的特性是,无论文件名是什么(即使包含嵌入的空格、制表符、换行符和任何字符),这个文件名都严格是一个参数。xargs
除非使用不可移植的黑客, 情况并非如此。 最后;
是结束该-exec
子句。 它的结尾需要定界的原因是其他find
选项可能跟在它-exec
后面,尽管很少这样做。 例如:
find . -name "*.js" -exec ls -l {} \; -name "special*" -exec wc -l {} \;
此命令的一个问题是它不会忽略非纯文本文件,因此可能会提示用户删除套接字、块和字符设备、管道和目录。即使您回答是,它也会因后者而失败。
另一个问题(虽然在这里并不是很关键)是,rm
对于每个大小为零的文件,都会调用 。如果将-exec
结尾从替换/;
为+
,将通过仅调用尽可能少的次数(通常仅调用一次)来find
优化子进程的创建。rm
以下是我修改此命令的方法:
find . -type f -size 0 -exec rm -i {} +
答案2
当使用时find -exec
,{}
会扩展到找到的每个结果。
例如,如果你有一个example
包含 3 个文件的目录a.txt
,b.txt
和c.txt
,find example/ -exec rm -i {} \;
将扩展为:
find example/ -exec rm -i example/a.txt \;
find example/ -exec rm -i example/b.txt \;
find example/ -exec rm -i example/c.txt \;
\;
末尾的 只是一个简单的转义符,表示;
exec 模式的结束。否则,它将由 shell 本身解释。
答案3
与find
命令的exec
选项一起,{}
部分将替换为执行命令时找到的文件的名称。\;
也很重要,因为这定义了正在执行的命令的结束
例如
find ~ -name \*.bak -exec -rm -f {} \;
将删除.bak
用户主目录或其中包含的文件夹中以结尾的所有文件。通过rm -f
对找到的每个文件执行。
xargs
接受标准输入行(通常来自管道),并在执行您给出的命令时从中形成参数的尾部
答案4
这是一个老问题,但我想补充更多信息:
find ./ -size 0 -exec rm -i {} \;
在上一个命令中,\;
是转义的分号。这可以防止 shell 处理该命令(即,通常;
会分隔命令)。
参数-exec
将所有内容解释为命令,直到转义的分号为止\;
(即,rm -i {}
它将是要执行的内部命令find
)。在内部命令中,{}
表示参数扩展。在英语中,它的意思是“插入在此处找到的文件名”。
因此,如果找到的文件是“file-a.txt”和“file-b.txt”,则将find
执行。rm -i file-a.txt
rm -i file-b.txt
此命令的一个问题是它不会忽略非纯文本文件,因此可能会提示用户删除套接字、块和字符设备、管道和目录。即使您回答是,它也会始终失败(即需要递归删除目录)
另一个问题(虽然在这里并不是很关键)是,rm
对于每个大小为零的文件,都会调用 。如果将-exec
结尾从替换/;
为, find 将通过仅调用尽可能少的次数(通常仅调用一次)来+
优化子进程的创建。rm
以下是我修改此命令的方法:
find ./ -type f -size 0 -exec rm -i {} +
curly brackets
或braces
:{}
可以以不同的方式使用
括号扩展
大括号可用于构建序列:
### prints out the numbers from 0 to 10
echo {0..10}
## prints out the same numbers, but in reverse order
echo {10..0}
## prints every second number, from 10 to 0
echo {10..0..2}
## prints every second letter, from z and working its way backwards to a.
echo {z..a..2}
我们也可以组合两个或多个序列:
## prints out a pair of letters, from aa to zz.
echo {a..z}{a..z}
添加前缀和后缀:
### adds '"' as prefix and suffix
echo \"{These,words,are,quoted}\"
# output: "These" "words" "are" "quoted"
# concatenates the files file1, file2, and file3 into combined_file.
cat {file1,file2,file3} > combined_file
# copies "file22.txt" to "file22.backup"
cp file22.{txt,backup}
笔记:
大括号内不允许有空格,{...}
除非空格是引或者逃脱。
echo {file1,file2}\ :{\ A," B",' C'}
# output: file1 : A file1 : B file1 : C file2 : A file2 : B file2 : C
扩展括号扩展。
括号可用于构建数组。Bash 中的数组定义方式是将元素放在括号之间()
,并使用空格分隔每个元素,如下所示:
month=("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec")
要访问数组中的元素,请使用括号内的索引[]
:
# Array indexes start at [0], so [3] points to the fourth item
$ echo ${month[3]}
## output: Apr
因此,我们可以创建一个类似这样的数组:
## builds an array that contains all the 2-letter combinations of the entire alphabet.
letter_combos=({a..z}{a..z})
## contains all the binary numbers for an 8-bit register, in ascending order,
## from 00000000, 00000001, 00000010, etc., to 11111111.
dec2bin=({0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1}{0..1})
最后一个特别有趣,因为 dec2bin 现在我们可以使用它来构建一个 8 位十进制到二进制转换器。假设您想知道 25 的二进制数是多少。您可以这样做:
$ echo ${dec2bin[25]}
## output: 00011001
但是 Teo,没有更好的方法将十进制转换为二进制吗?
- 是的,有,但是还是很有趣的,对吧?
分组命令
{ ... }
可用于放置要在当前 shell 上下文中执行的命令列表。否子壳已创建。列表后面的分号;
(或换行符)是必需的。
括号()
用于在子壳:
menu_type=bar
echo $menu_type
## output: bar
## new lets called in a sub-shell
(menu_type=superbar; echo $menu_type)
## output: superbar
## back to the context
echo $menu_type
## output: bar
我们无法获取superbar
的新值menu_type
。
但是,如果我们运行如下命令:
{ menu_type=superbar; echo $menu_type; }
## output: superbar
echo $menu_type
## output: superbar
{ ... }
没有创建子 shell,这就是我们可以访问该menu_type
值的原因。
{ ... }
也称为inline group
,实际上,它创建了一个匿名函数(即没有名称的函数)。简单地说,与“标准”函数不同, 中的变量{ ... }
对脚本的其余部分仍然可见。
此外,{ ... }
还可用于将多个命令的输出分组到其中stdout
或接收重定向到其中stdin
。让我们看一个例子:
#!/bin/bash
# rpm-check.sh
# Queries an rpm file for description, listing, and whether it can be installed.
# Saves output to a file.
SUCCESS=0
E_NOARGS=65
if [ -z "$1" ]; then
echo "Usage: `basename $0` rpm-file"
exit $E_NOARGS
fi
{ # Begin command group.
echo
echo "Archive Description:"
rpm -qpi $1 # Query description.
echo
echo "Archive Listing:"
rpm -qpl $1 # Query listing.
echo
rpm -i --test $1 # Query whether rpm file can be installed.
if [ "$?" -eq $SUCCESS ]
then
echo "$1 can be installed."
else
echo "$1 cannot be installed."
fi
echo # End command group.
} > "$1.test" # Redirects output of everything in block to file.
echo "Results of rpm test in file $1.test"
exit 0
现在,让我们看看如何在组中进行 I/O 重定向stdin
:
#!/bin/bash
File=/etc/fstab
## reads the first two lines of the file
{
read line1
read line2
} < $File
echo "First line in $File is:"
echo "$line1"
echo
echo "Second line in $File is:"
echo "$line2"
exit 0
将命令组的输出保存到文件的另一个示例
## exec commands sequentially and redirects the output of the ls command into the png-list.txt file
echo "I found all these png files:"; find . -iname "*.png"; echo "Within this bunch of files:"; ls > png-list.txt
## exec commands sequentially and redirects the output of the group into the png-list.txt file
{ echo "I found all these png files:"; find . -iname "*.png"; echo "Within this bunch of files:"; ls; } > png-list.txt
有什么区别,Teo?
好吧,年轻的徒弟。第二个创建了包含所有输出的文件png-list.txt
,从行开始“I found all these png files:“
,到命令输出结束ls
。
子 shell 攻击
{ ... }
Bash 为括号组命令创建子 shell当且仅当它是管道的一部分,例如:
$ { A=1; { A=2; sleep 2; } ; echo $A; }
## output: 2
$ { A=1; { A=2; sleep 2; } | sleep 1; echo $A; }
## output: 1
笔记:
括号和括号内的命令列表之间有空格。这是因为{
和}
是保留字(即内置于 shell 中的命令)。此外,命令列表必须以分号结尾;
或使用换行符来分隔命令。
参数扩展
好的,回到
month=("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec")
echo ${month[3]}
## output: Apr
此处括号{}
并非作为序列生成器的一部分使用,而是作为生成参数扩展的一种方式。参数扩展涉及框上的内容:
它采用括号内的变量或表达式并将其扩展为其所代表的内容。
Teo,这是什么意思?
嗯,它的意思是${...}
告诉 shell 扩展其中的内容。在本例中,month
是我们之前定义的数组,即:
month=("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec")
并且,3
数组中的 item 指向"Apr"
(即 Bash 中数组中的第一个索引是[0]
)。这意味着echo ${month[3]}
,在扩展之后,转换为 echo "Apr"
。
将变量解释为其值是扩展它的一种方法,但我们还可以利用其他几种方法。我们可以使用参数扩展来操纵从变量中读取的内容(即通过从末尾截断一部分)。
假设你有一个如下变量:
a="This sentence is too longgg"
## chops off the last two gs
echo ${a%gg}
## output: This sentence is too long
这对于将文件从一种格式转换为另一种格式非常有用。例如,如果我们有一个命令,它将 JPEG 图像image.jpg
转换为 PNG 图像image.png
:
convert image.jpg image.png
我们可以像这样重写它:
i='image.jpg'
## chops off the extension 'jpg' and adds 'png'
convert $i "${i%jpg}png"
## output: convert image.jpg image.png
但是,这怎么会比仅仅写入文件名称更有用呢?
好吧,当我们有一个包含数百张 JPEG 图像的目录时,您需要将其转换为 PNG,只需在其中运行以下命令:
for i in *.jpg; do convert $i ${i%jpg}png; done
…所有图片都会自动转换。欢迎你,年轻的徒弟。
如果需要从变量的开头切掉一块,%
请使用而不是#
:
$ a="Hello World!"
## chops off the word 'Hello' and adds 'Goodbye'
$ echo Goodbye${a#Hello}
## output: Goodbye World!
文本占位符
在之后使用xargs -i
(即替换字符串选项)。{}
双花括号是输出文本的占位符。
## Execute 'echo ./<file>' for each file in the directory
ls . | xargs -i -t echo ./{} $1
# ^^ ^^
路径名
路径名是包含完整路径的文件名。例如,/home/<user>/Notes/todo.txt
。这有时被称为绝对路径。我们{}
主要会在find
包含的构造中遇到-exec <command> \;
。但是,这不是 shell 内置命令。如果<command>
包含{}
,则 find 会用所选文件的完整路径名替换"{}"
。
# Removes all core dump files from user's home directory.
find ~/ -name 'core*' -exec rm {} \;