使用以下帮助脚本允许使用find ... -exec ./script '{}' l1 l2 \; 的强大功能来定位目标文件并在每个文件中交换行l1 和l2。 (它要求文件中没有相同的重复行在搜索范围内)该脚本使用sed 将每个文件中的两个交换行读取到索引数组中并传递这些行到sed通过匹配完成交换。 sed 调用使用其“匹配的第一个地址”状态 将第二个表达式交换限制为第一次出现。下面是使用帮助程序脚本在所有匹配文件中交换行 5 和 15 的示例:
find . -maxdepth 1 -type f -name "lnum*" -exec ../swaplines.sh '{}' 5 15 \;
例如,上面的find调用在当前目录中找到了文件lnumorig.txt和lnumfile.txt,最初包含:
$ head -n20 lnumfile.txt.bak
1 A simple line of test in a text file.
2 A simple line of test in a text file.
3 A simple line of test in a text file.
4 A simple line of test in a text file.
5 A simple line of test in a text file.
6 A simple line of test in a text file.
<snip>
14 A simple line of test in a text file.
15 A simple line of test in a text file.
16 A simple line of test in a text file.
17 A simple line of test in a text file.
18 A simple line of test in a text file.
19 A simple line of test in a text file.
20 A simple line of test in a text file.
并按预期交换了 5 和 15 行:
$ head -n20 lnumfile.txt
1 A simple line of test in a text file.
2 A simple line of test in a text file.
3 A simple line of test in a text file.
4 A simple line of test in a text file.
15 A simple line of test in a text file.
6 A simple line of test in a text file.
<snip>
14 A simple line of test in a text file.
5 A simple line of test in a text file.
16 A simple line of test in a text file.
17 A simple line of test in a text file.
18 A simple line of test in a text file.
19 A simple line of test in a text file.
20 A simple line of test in a text file.
帮助脚本本身是:
#!/bin/bash
[ -z $1 ] && { # validate requierd input (defaults set below)
printf "error: insufficient input calling '%s'. usage: file [line1 line2]\n" "${0//*\//}" 1>&2
exit 1
}
l1=${2:-10} # default/initialize line numbers to swap
l2=${3:-15}
while IFS=$'\n' read -r line; do # read lines to swap into indexed array
a+=( "$line" );
done <<<"$(sed -n $((l1))p "$1" && sed -n $((l2))p "$1")"
((${#a[@]} < 2)) && { # validate 2 lines read
printf "error: requested lines '%d & %d' not found in file '%s'\n" $l1 $l2 "$1"
exit 1
}
# swap lines in place with sed (remove .bak for no backups)
sed -i.bak -e "s/${a[1]}/${a[0]}/" -e "0,/${a[0]}/s/${a[0]}/${a[1]}/" "$1"
exit 0
尽管我没有设法在单行中完成所有工作,但我还是认为值得发布它,以防您可以利用它或从中汲取灵感。 注意:如果您确实使用了它,请在将其释放到您的系统之前进行测试以使其满意。该脚本当前使用sed -i.bak ... 来创建为测试目的而更改的文件的备份。当您满意它满足您的需求时,您可以删除.bak。
如果您不需要设置默认行来交换帮助程序脚本本身,那么我会将第一个验证检查更改为 [ -z $1 -o -z $2 -o $3 ] 以确保在脚本运行时提供所有所需的参数调用。
虽然它确实通过数字标识要交换的行,但它依靠每行的直接匹配来完成交换。这意味着直到交换范围末尾的任何相同的重复行都将导致意外匹配并且无法交换预期的行。这是不将每一行存储在要交换的行范围内所施加的限制的一部分,如 cmets 中所讨论的。这是一个权衡。有很多很多方法可以解决这个问题,所有方法都有其优点和缺点。如果您有任何问题,请告诉我。
蛮力法
根据您的评论,我修改了帮助脚本以使用蛮力复制/交换方法,该方法将消除搜索范围内任何重复行的问题。该帮助程序通过sed 获取行,如原始行一样,但随后读取从file 到tmpfile 的所有行,并在遇到时交换适当编号的行。 tmpfile填完后,复制到原来的file,去掉tmpfile。
#!/bin/bash
[ -z $1 ] && { # validate requierd input (defaults set below)
printf "error: insufficient input calling '%s'. usage: file [line1 line2]\n" "${0//*\//}" 1>&2
exit 1
}
l1=${2:-10} # default/initialize line numbers to swap
l2=${3:-15}
while IFS=$'\n' read -r line; do # read lines to swap into indexed array
a+=( "$line" );
done <<<"$(sed -n $((l1))p "$1" && sed -n $((l2))p "$1")"
((${#a[@]} < 2)) && { # validate 2 lines read
printf "error: requested lines '%d & %d' not found in file '%s'\n" $l1 $l2 "$1"
exit 1
}
# create tmpfile, set trap, truncate
fn="$1"
rmtemp () { cp "$tmpfn" "$fn"; rm -f "$tmpfn"; }
trap rmtemp SIGTERM SIGINT EXIT
declare -i n=1
tmpfn="$(mktemp swap_XXX)"
:> "$tmpfn"
# swap lines in place with a tmpfile
while IFS=$'\n' read -r line; do
if ((n == l1)); then
printf "%s\n" "${a[1]}" >> "$tmpfn"
elif ((n == l2)); then
printf "%s\n" "${a[0]}" >> "$tmpfn"
else
printf "%s\n" "$line" >> "$tmpfn"
fi
((n++))
done < "$fn"
exit 0