【问题标题】:Using a variable in sed search pattern when the value of the variable contains square brackets当变量的值包含方括号时,在 sed 搜索模式中使用变量
【发布时间】:2015-12-17 02:21:12
【问题描述】:

我要做的是检查文件是否已创建。我能想到的最好方法是事先列出文件,之后列出它们,从后列表中删除前列表,然后查看后列表是否不为零。我在从后列表中删除前列表时遇到了麻烦。没有从列表中删除带方括号的文件名。

while read -r LINE                                                              
do                                                                              
        sed -i -- "/$LINE/d" listfilesafter.swp #without the -- I get 'sed: 1: "listfilesafter.swp": extra characters at the end of l command'
        rm listfilesafter.swp--                                                 
done < listfilesbefore.swp 

如果我使用 '' 则不会调用该变量,并且 read 上的 -r 选项似乎不会使其像我预期的那样工作。如果有人对这样做的替代方法有任何建议,请做出贡献,但我仍然想知道当变量的值包含元字符时如何在搜索模式中使用变量。如果有人可以帮助消除“rm listfilesafter.swp--”的代码气味,那也将不胜感激。完整代码如下:

cd ~/Desktop
ls >listfilesbefore.swp
#echo "balh blah" >SomeNonZeroFile.txt #comment or uncomment to test the if then statement
ls >listfilesafter.swp
sed -i -- '/listfilesafter.swp/d' listfilesafter.swp #deletes listfilesafter.swp from the list of files create after the event on line 3
while read -r LINE
do
        sed -i -- "/$LINE/d" listfilesafter.swp #without the -- I get 'sed: 1: "listfilesafter.swp": extra characters at the end of l command'
        rm listfilesafter.swp--
done < listfilesbefore.swp
cat listfilesafter.swp
echo "check listfiles. Enter to continue."
read dummy_variable
if [ -s listfilesafter.swp ]
then
        rm listfilesbefore.swp
        rm listfilesafter.swp
        echo "success, the file was created"
else
        rm listfilesbefore.swp
        rm listfilesafter.swp
        echo "failure, the file was not created"
fi

【问题讨论】:

  • 你可以看看这里:stackoverflow.com/a/4717415/2082964
  • 这似乎是一种测试文件是否被创建的扭曲方式。为什么不简单地查找应该创建的名称? (您应该使用trap 来确保删除临时文件。您还应该为临时文件使用名称,以便同时运行脚本的两个人不会相互干扰。例如,使用listfilebefore.$$.swp嵌入进程 ID,或使用mktemp(命令)创建文件。
  • @JonathanLeffler 我无法检查文件名,因为在我的真实脚本中它总是不同的。带有进程 ID 的良好调用。我会调查trapmktemp

标签: regex macos bash sed


【解决方案1】:

鉴于您有两个按排序顺序排列的文件列表(因为ls 按排序顺序列出文件),您可能应该使用类似diff 的命令,或者在这种情况下, comm 查找两个文件列表之间的差异。

如果您想知道创建了哪些文件,那么这就是第二个文件中不在第一个文件中的文件(行)的列表。如果没有选项,comm 会在 3 列中列出它读取的行:

  • 第一个文件中的行不在第二个文件中
  • 第二个文件中的行不在第一个文件中
  • 两个文件中的行

您只需要第二列中的行(文件名),因此您想要隐藏第一列和第三列中的文件列表,因此您将使用comm -13 来执行此操作:

before=$(mktemp ${TMPDIR:-/tmp}/files.XXXXXX)
after=$(mktemp ${TMPDIR:-/tmp}/files.XXXXXX)

trap "rm -f $before $after; exit 1" 0 1 2 3 13 15

ls > $before
…execute command that creates file(s)…
ls > $after
comm -13 $before $after

rm -f $before $after
trap 0

显然,您可以在变量中捕获来自comm 的文件列表以供进一步分析等。


当搜索字符串包含元字符时使sed 工作

我仍然对 sed 感到困惑。如果值包含元字符,如何在 sed 的搜索模式中使用变量?或者在这种情况下,我最好使用sed以外的其他东西?

在您的情况下,最好不要使用sed,而且无论如何,如果目录中有成百上千个文件(每个文件名运行一次sed),您的技术就会非常缓慢不会很快)。

但是,假设有必要使用sed 并且您想处理列表中文件名中的元字符,那么您必须转义元字符(前面带有反斜杠)。我可能会做这样的事情:

sed 's/[][\/*.]/\\&/g; s%.*%/^&$/d%' listfilesbefore.swp > script.sed
sed -f script.sed listfilesafter.swp

第一个脚本采用行中的任何元字符(文件名)并将其替换为反斜杠元字符。在第一个替换中,[][\/*.] 字符类匹配方括号、两种类型的斜线、星号和点。根据您使用的sed 变体的偏好,您可能还需要用反斜杠保护(){},但在POSIX 标准sed 中,{} 在以反斜杠为前缀时获得元字符含义,所以默认情况下它们不会被修改。第二个替代项采用可能修改的行并将其转换为“匹配和删除”命令。因此,输出是一个sed 脚本,它将删除在listfilesbefore.swp 中找到的文件名。第二个命令将该脚本应用于listfilesafter.swp,在一个sed 命令中执行大纲代码对每个文件名运行一次sed 的操作。

使用sed 生成sed 脚本是一项强大的技术。它并不总是合适的,但当它合适时,它非常有用。

Shell 脚本demo.sh

echo "Pre-populate the directory with some random file names"
for file in $(random -n 20 -T '%W%V%C-%w%v%c%v%c-%04[0000:9999]d.txt')
do
    cp /dev/null $file
done

for template in '%w%v%w(%03[000:999]d)%w%v%w.txt' \
                '%w%v%w[123]%w%v%we.txt' \
                '%w%v%wfile*%03[0:999]d*.txt' \
                '%w%v%w%v%c\\\%d.txt' \
                '%w%v%w-{%04X}-{%04X}.txt'
do
    for file in $(random -n 2 -T "$template")
    do
        cp /dev/null "$file"
    done
done

ls > listfilesbefore.swp
ls

echo
echo "Create some new files with metacharacters in the names"
for file in 'new(123)file.txt' 'new[123]file.txt' 'newfile*321*.txt' \
            'newfile\\\.txt' 'newfile-{A39F}-{B77D}.txt'
do
    cp /dev/null "$file"
done

ls
ls > listfilesafter.swp

echo
echo "Create sed script"

sed 's/[][\/*.]/\\&/g; s%.*%/^&$/d%' listfilesbefore.swp > script.sed

echo
cat script.sed

echo
echo "Apply it"
sed -f script.sed listfilesafter.swp

我使用的random 命令是我自己设计的,但是对于这样的演示很方便。

示例运行

Pre-populate the directory with some random file names
AIG-taral-3486.txt
COV-oipuc-9088.txt
CUG-vowan-5758.txt
FEH-ieqek-0603.txt
IUS-aaduw-7080.txt
KER-jazuc-4824.txt
MIZ-iezec-8255.txt
NIT-kupib-6873.txt
PUX-oocov-2216.txt
QAW-xonod-3937.txt
QES-wawok-4790.txt
RON-difag-1986.txt
SAD-gesug-5706.txt
SAJ-luqoj-4311.txt
TUZ-wapaw-8547.txt
VAL-zutap-8054.txt
YIP-xudeb-7397.txt
YUP-uudiv-8848.txt
ZIB-jurax-2903.txt
ZUR-xonik-8800.txt
aavfile*147*.txt
demo.sh
diman\\\7115.txt
ganur\\\8732.txt
gud-{7049}-{3103}.txt
listfilesbefore.swp
lur[123]maee.txt
rivfile*065*.txt
ueo(417)yea.txt
uoi(751)qio.txt
woi-{37E8}-{009C}.txt
xof[123]hoxe.txt

Create some new files with metacharacters in the names
AIG-taral-3486.txt
COV-oipuc-9088.txt
CUG-vowan-5758.txt
FEH-ieqek-0603.txt
IUS-aaduw-7080.txt
KER-jazuc-4824.txt
MIZ-iezec-8255.txt
NIT-kupib-6873.txt
PUX-oocov-2216.txt
QAW-xonod-3937.txt
QES-wawok-4790.txt
RON-difag-1986.txt
SAD-gesug-5706.txt
SAJ-luqoj-4311.txt
TUZ-wapaw-8547.txt
VAL-zutap-8054.txt
YIP-xudeb-7397.txt
YUP-uudiv-8848.txt
ZIB-jurax-2903.txt
ZUR-xonik-8800.txt
aavfile*147*.txt
demo.sh
diman\\\7115.txt
ganur\\\8732.txt
gud-{7049}-{3103}.txt
listfilesbefore.swp
lur[123]maee.txt
new(123)file.txt
new[123]file.txt
newfile*321*.txt
newfile-{A39F}-{B77D}.txt
newfile\\\.txt
rivfile*065*.txt
ueo(417)yea.txt
uoi(751)qio.txt
woi-{37E8}-{009C}.txt
xof[123]hoxe.txt

Create sed script

/^AIG-taral-3486\.txt$/d
/^COV-oipuc-9088\.txt$/d
/^CUG-vowan-5758\.txt$/d
/^FEH-ieqek-0603\.txt$/d
/^IUS-aaduw-7080\.txt$/d
/^KER-jazuc-4824\.txt$/d
/^MIZ-iezec-8255\.txt$/d
/^NIT-kupib-6873\.txt$/d
/^PUX-oocov-2216\.txt$/d
/^QAW-xonod-3937\.txt$/d
/^QES-wawok-4790\.txt$/d
/^RON-difag-1986\.txt$/d
/^SAD-gesug-5706\.txt$/d
/^SAJ-luqoj-4311\.txt$/d
/^TUZ-wapaw-8547\.txt$/d
/^VAL-zutap-8054\.txt$/d
/^YIP-xudeb-7397\.txt$/d
/^YUP-uudiv-8848\.txt$/d
/^ZIB-jurax-2903\.txt$/d
/^ZUR-xonik-8800\.txt$/d
/^aavfile\*147\*\.txt$/d
/^demo\.sh$/d
/^diman\\\\\\7115\.txt$/d
/^ganur\\\\\\8732\.txt$/d
/^gud-{7049}-{3103}\.txt$/d
/^listfilesbefore\.swp$/d
/^lur\[123\]maee\.txt$/d
/^rivfile\*065\*\.txt$/d
/^ueo(417)yea\.txt$/d
/^uoi(751)qio\.txt$/d
/^woi-{37E8}-{009C}\.txt$/d
/^xof\[123\]hoxe\.txt$/d

Apply it
listfilesafter.swp
new(123)file.txt
new[123]file.txt
newfile*321*.txt
newfile-{A39F}-{B77D}.txt
newfile\\\.txt

【讨论】:

  • 非常有用的答案,但我仍然对 sed 感到困惑。如果值包含元字符,如何在 sed 的搜索模式中使用变量?或者在这种情况下,我最好使用 sed 以外的东西吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-01-21
  • 2021-12-01
  • 1970-01-01
  • 2016-07-28
  • 1970-01-01
  • 1970-01-01
  • 2023-01-24
相关资源
最近更新 更多