如果文件中不存在这些行,如何将多行追加到文件中?

如果文件中不存在这些行,如何将多行追加到文件中?

如果文件中不存在这些行,如何将多行追加到文件中?

例如,要添加多个全局别名,/etc/bash.bashrc我使用此处文档:

cat <<-"BASHRC" >> /etc/bash.bashrc
    alias rss="/etc/init.d/php*-fpm restart && systemctl restart nginx.service"
    alias brc="nano /etc/bash.bashrc"
BASHRC

我被批评说这个操作不包括检查行是否已经存在的方法,如果错误地重新执行这里文档,我可能会导致冗余和冲突。

答案1

简单的 shell 脚本,用于将文件中的行添加newdatadatafile.更改newdata为此处文档应该很简单。这实际上不是很有效,因为它grep需要每个(新的)输入行:

target=datafile
while IFS= read -r line ; do
    if ! grep -Fqxe "$line" "$target" ; then
        printf "%s\n" "$line" >> "$target"
    fi
done < newdata 

对于每一行,我们用于grep查看它是否已存在于目标文件中、-F用于固定字符串匹配(无正则表达式)、-x用于全行匹配以及-q抑制匹配行的输出。grep如果找不到匹配行,则返回虚假错误代码,因此如果否定结果为真,则附加到目标文件。


更有效的是,在awk.这依赖于awk能够将任意行处理为数组的键。

$ awk 'FNR == NR { lines[$0] = 1; next } ! ($0 in lines) {print}' datafile newdata 

第一部分FNR == NR { lines[$0] = 1; next }将第一个输入文件的所有行作为键加载到(关联)数组中lines。第二部分! ($0 in lines) {print}在以下输入行上运行,如果该行不在数组中,则打印该行,即“新”行。

生成的输出仅包含新行,因此需要将其附加到原始文件,例如sponge

$ awk 'FNR == NR { lines[$0] = 1; next } ! ($0 in lines) {print}' datafile newdata | sponge -a datafile

或者我们可以将awk这些行附加到最后一行,它只需要将文件名传递给awk

$ target=datafile 
$ awk -vtarget="$target" 'FNR == NR { lines[$0] = 1; next } 
                        ! ($0 in lines) {print >> target}' "$target" newdata

要将here-doc与 一起使用,除了设置重定向之外,awk我们还需要添加(stdin)作为显式源文件,因此-awk ... "$target" - <<EOF

答案2

这个解决方案可能与您的想法有点不同,但我会检查是否实际定义了别名,如果没有,则仅将其添加到 bashrc 中。假设您使用的是相对最新版本的 Bash...

# instead of 'alias x=y' form put them in an associative array
declare -A aliases
aliases['a']='apple'
aliases['ba']='banana'

for key in "${!aliases[@]}"; do
    if ! alias "$key" > /dev/null 2>&1; then
        printf "alias %s='%s'\n" "$key" "${aliases[$key]}" >> /etc/bash.bashrc
    fi
done

unset aliases key

获取它. ./scriptname而不是作为普通脚本运行它。

笔记:如果您有一堆候选别名,并且对手动将它们转换为关联数组形式持谨慎态度,那么您可以使用以下命令编辑文件vim并运行此命令::%s/\valias *([^=]+)\=['"]?([^'"]+)['"]?$/aliases['\1']='\2'/

或者将您的候选别名(而不是其他任何东西)放入文件(例如/tmp/aliases.txt)中,并将aliases[..]='..'脚本中的行替换为:

while IFS= read -r a; do 
    eval "$a"; 
done < <(sed -E "s/alias  *([^=]+)=['\"]?([^'\"]+)['\"]?$/aliases['\1']='\2'/" /tmp/aliases.txt)

当然,除了使用 AA 之外,还有其他方法,但它们干净且易于使用……我现在全力以赴。 :)

答案3

精确匹配的通用解决方案(未针对性能进行优化);该文件input包含要检查的行:

awk 'FNR==NR { lines[NR]=$0; next; };
    { for(i=1;i<=length(lines);i++) if ($0==lines[i]) matches[i]=1; print; };
    END { for(i=1;i<=length(lines);i++)
        if (matches[i]==0) print lines[i]; }' input file

测试:

:> cat input
alias rss="/etc/init.d/php*-fpm restart && systemctl restart nginx.service"
alias brc="nano /etc/bash.bashrc"

:> cat file
a
b
c
alias rss="/etc/init.d/php*-fpm restart && systemctl restart nginx.service"
d

命令的输出awk

a
b
c
alias rss="/etc/init.d/php*-fpm restart && systemctl restart nginx.service"
d
alias brc="nano /etc/bash.bashrc"

答案4

嗯嗯。这个怎么样...

将要添加的别名放入文件中新的。然后...

cp bashrc bashrc.tmp && comm -23 <(sort -u new) <(sort -u bashrc.tmp) >> bashrc && rm -f bashrc.tmp

我们对新别名和bashrc内容(放入临时文件以避免当我们附加到时出现竞争情况bashrc)并运行它们commcomm对排序的文件进行逐行比较,并显示独特的行和共同的行。我们抑制第 2 列和第 3 列 ( -23),因此结果只是那些唯一的行新的我们将它们附加到bashrc。就是这样。

(这是一种与我的其他答案完全不同的方法,专注于您对更多“一句台词”的追求。)

相关内容