bash 中可以交换两个文件吗?
或者,可以用比这更短的方式交换它们吗:
cp old tmp
cp curr old
cp tmp curr
rm tmp
$ mv old tmp && mv curr old && mv tmp curr
效率稍微高一点!
包装成可重用的shell函数:
function swap()
{
local TMPFILE=tmp.$$
mv "$1" $TMPFILE && mv "$2" "$1" && mv $TMPFILE "$2"
}
将其添加到您的 .bashrc 中:
function swap()
{
local TMPFILE=tmp.$$
mv "$1" $TMPFILE
mv "$2" "$1"
mv $TMPFILE "$2"
}
如果您想处理中间
mv
操作的潜在失败,请检查 Can Bal 的答案。
请注意,这个答案和其他答案都没有提供“原子”解决方案,因为不可能使用 Linux 系统调用和/或流行的 Linux 文件系统来实现这种解决方案。对于 Darwin 内核,请检查 exchangedata
系统调用。
mv new old -b
你会得到:
old and old~
如果你想拥有
old and old.old
您可以使用 -S 将 ~ 更改为您的自定义后缀
mv new old -b -S .old
ls
old old.old
使用这种方法,您实际上可以更快地交换它们,仅使用 2 mv:
mv new old -b && mv old~ new
function swap()
{
tmpfile=$(mktemp $(dirname "$1")/XXXXXX)
mv "$1" "$tmpfile" && mv "$2" "$1" && mv "$tmpfile" "$2"
}
$HOME/bin/swapfiles
)。我认为它对坏事的抵抗力相对较强。
#!/bin/bash
if [ "$#" -ne 2 ]; then
me=`basename $0`
echo "Syntax: $me <FILE 1> <FILE 2>"
exit -1
fi
if [ ! -f $1 ]; then
echo "File '$1' does not exist!"
fi
if [ ! -f $2 ]; then
echo "File '$2' does not exist!"
fi
if [[ ! -f $1 || ! -f $2 ]]; then
exit -1
fi
tmpfile=$(mktemp $(dirname "$1")/XXXXXX)
if [ ! -f $tmpfile ]; then
echo "Could not create temporary intermediate file!"
exit -1
fi
# move files taking into account if mv fails
mv "$1" "$tmpfile" && mv "$2" "$1" && mv "$tmpfile" "$2"
function swap()
{
if [ ! -z "$2" ] && [ -e "$1" ] && [ -e "$2" ] && ! [ "$1" -ef "$2" ] && (([ -f "$1" ] && [ -f "$2" ]) || ([ -d "$1" ] && [ -d "$2" ])) ; then
tmp=$(mktemp -d $(dirname "$1")/XXXXXX)
mv "$1" "$tmp" && mv "$2" "$1" && mv "$tmp"/"$1" "$2"
rmdir "$tmp"
else
echo "Usage: swap file1 file2 or swap dir1 dir2"
fi
}
这适用于 Linux。不确定 OS X。
function swp2file()
{ if [ $1 != $2 ] ; then
local TMPFILE=tmp.$$
mv "$1" $TMPFILE
mv "$2" "$1"
mv $TMPFILE "$2"
else
echo "swap requires 2 different filename"
fi
}
再次感谢哈迪;-)
接下来的诱惑是实现 shell 函数 swap() 或类似的函数。如果您确实要格外小心地检查错误代码。可能具有可怕的破坏性。还需要检查预先存在的 tmp 文件。
我合并使用
basename
和
dirname
来保持文件名完整*。swap() {
if (( $# == 2 )); then
mv "$1" /tmp/
mv "$2" "`dirname $1`"
mv "/tmp/`basename $1`" "`dirname $2`"
else
echo "Usage: swap <file1> <file2>"
return 1
fi
}
我已经在 bash 和 zsh 中对此进行了测试。
*所以要澄清这是如何更好的:
如果您开始:
dir1/file2: this is file2
dir2/file1: this is file1
最终会是:
dir1/file2: this is file1
dir2/file1: this is file2
内容已交换,但文件名保持不变
。我的解决方案做到了:
dir1/file1: this is file1
dir2/file2: this is file2
内容和
名称互换。
swap
脚本,带有偏执的错误检查,以避免不太可能发生的失败情况。
如果任何操作失败,则会报告。
#!/bin/sh
if [ -z "$1" ] || [ -z "$2" ]; then
echo "Expected 2 file arguments, abort!"
exit 1
fi
if [ ! -z "$3" ]; then
echo "Expected 2 file arguments but found a 3rd, abort!"
exit 1
fi
if [ ! -f "$1" ]; then
echo "File '$1' not found, abort!"
exit 1
fi
if [ ! -f "$2" ]; then
echo "File '$2' not found, abort!"
exit 1
fi
# avoid moving between drives
tmp=$(mktemp --tmpdir="$(dirname '$1')")
if [ $? -ne 0 ]; then
echo "Failed to create temp file, abort!"
exit 1
fi
# Exit on error,
mv "$1" "$tmp"
if [ $? -ne 0 ]; then
echo "Failed to to first file '$1', abort!"
rm "$tmp"
exit 1
fi
mv "$2" "$1"
if [ $? -ne 0 ]; then
echo "Failed to move first file '$2', abort!"
# restore state
mv "$tmp" "$1"
if [ $? -ne 0 ]; then
echo "Failed to move file: (unable to restore) '$1' has been left at '$tmp'!"
fi
exit 1
fi
mv "$tmp" "$2"
if [ $? -ne 0 ]; then
# this is very unlikely!
echo "Failed to move file: (unable to restore) '$1' has been left at '$tmp', '$2' as '$1'!"
exit 1
fi
mv
而不是
cp
?d_swap lfile rfile
GNU mv 有 -b 和 -T 开关。您可以使用 -T 处理目录 开关。
引号用于带空格的文件名。
这有点冗长,但我已经在文件和目录中多次使用过它。在某些情况下,您可能希望使用目录所具有的名称重命名文件,但这不是由此函数处理的。
如果您只想重命名文件(将它们保留在原来的位置),这不是很有效,最好使用 shell 变量来完成。
d_swap() {
test $# -eq 2 || return 2
test -e "$1" || return 3
test -e "$2" || return 3
if [ -f "$1" -a -f "$2" ]
then
mv -b "$1" "$2" && mv "$2"~ "$1"
return 0
fi
if [ -d "$1" -a -d "$2" ]
then
mv -T -b "$1" "$2" && mv -T "$2"~ "$1"
return 0
fi
return 4
}
此功能将重命名文件。它使用临时名称(在名称前面放置一个点“.”),以防文件/目录位于同一目录中(通常是这种情况)。
d_swapnames() {
test $# -eq 2 || return 2
test -e "$1" || return 3
test -e "$2" || return 3
local lname="$(basename "$1")"
local rname="$(basename "$2")"
( cd "$(dirname "$1")" && mv -T "$lname" ".${rname}" ) && \
( cd "$(dirname "$2")" && mv -T "$rname" "$lname" ) && \
( cd "$(dirname "$1")" && mv -T ".${rname}" "$rname" )
}
这要快得多(没有复制,只是重命名)。更是丑陋不堪。它会重命名任何东西:文件、目录、管道、设备。
rename_utils
包)。它的功能更强大,因为它允许您使用文本编辑器批量重命名批量文件,但具体而言,它可以检测并处理循环重命名。示例:
当然它太强大了,这就是我来这里的原因,但由于之前没有人提到过这一点,我想我应该补充一下。