如果文件中不存在这些行,如何将多行追加到文件中?
例如,要添加多个全局别名,/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 脚本,用于将文件中的行添加newdata
到datafile
.更改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)并运行它们comm
。comm
对排序的文件进行逐行比较,并显示独特的行和共同的行。我们抑制第 2 列和第 3 列 ( -23
),因此结果只是那些唯一的行新的我们将它们附加到bashrc。就是这样。
(这是一种与我的其他答案完全不同的方法,专注于您对更多“一句台词”的追求。)