我是 Bash 脚本的新手。我一直在尝试编写一个脚本,用于交换用户传递给它的两个文件的文件名。
以下是照片到目前为止我的剧本的两个版本
以下是文本格式的脚本mv
:
#! /bin/bash
file1=$file1
file2=$file2
echo "write file name :"
read file1 file2
if [[ $file1 = $file2 ]]
then
cp $file1 $file1
mv $file1 $file2
fi
if [[ $file2 = $file1 ]]
then
mv $file2 $file1
fi
但我的问题是,如果我可以编写一个脚本,让用户先写下 2 个文件名,然后脚本将交换这两个文件名
交换文件名的基本内容我读过的是这样的
cp $file1 temporaryfile
mv $file1 $file2
mv $file2 temporyfile
答案1
一种可能的方法是
不久前,我专门为此目的创建了一个函数,我将其保存在我的 中.bashrc
,并且可以改编成脚本。您应该利用位置参数以便用户可以在命令行上输入文件名。这是我的原始函数:
swap_files() {
if [ $# -ne 2 ]
then
echo "Usage: swap_files file1 file2"
else
local TMPFILE=$(mktemp)
mv -- "$1" "$TMPFILE"
mv -- "$2" "$1"
mv -- "$TMPFILE" "$2"
fi
}
您可以删除swap_files(){
声明、local
关键字和关闭}
,并将其转换为脚本 - 只需#!/bin/bash
在顶部添加即可。当然,有很多事情可以改进,但在最基本的层面上,这和交换一样简单(顺便说一下,在 C 中经常教交换数组项,但这只是一个切题)。
#!/bin/bash
if [ $# -ne 2 ]
then
printf "Usage: swap_files file1 file2\n" > /dev/stderr
else
TMPFILE=$(mktemp)
mv -- "$1" "$TMPFILE"
mv -- "$2" "$1"
mv -- "$TMPFILE" "$2"
fi
当然,如果文件名包含空格,请记住引用位置参数。如下所示:
swap_files 'file 1' 'file 2'
注意使用--
以避免文件名开头出现问题-
。更好的方法是养成使用 引用当前工作目录中文件的习惯./
,特别是如果您使用 globstar *
(globstar 与这个问题无关,但如果我们讨论的是开头为 的文件名,则值得一提-
)。此外,./
方法更易于移植,因为某些版本的mv
比如在 FreeBSD 上没有--
选择。
正如 terdon 在评论中所建议的,我们还可以在第一个文件的父文件夹中创建临时文件,以避免跨文件系统移动文件。
#!/bin/bash
if [ $# -ne 2 ]
then
printf "Usage: swap_files file1 file2\n" > /dev/stderr
else
file1_dir=${1%/*}
# just in case there were no slashes removed, assume cwd
if [ "$file1_dir" = "$1" ]; then
file1_dir="."
fi
tmpfile=$(mktemp -p "$file1_dir" )
mv -- "$1" "$tmpfile"
mv -- "$2" "$1"
mv -- "$tmpfile" "$2"
fi
你的脚本和需要改进的地方
1. 冗余变量赋值
file1=$file1
file2=$file2
这部分将$file1
变量分配给...file1
变量;这样做有两个问题 - 将变量分配给其自身是多余的,并且它一开始就不存在,脚本中先前没有该变量的声明。
2. 谨防使用 read 进行分词
如果您的用户尝试将带引号的项目放入您的read
命令中,将会发生以下情况:
$ read file1 file2
'one potato' 'two potato'
$ echo "$file1"
'one
$ echo "$file2"
potato' 'two potato'
根据 shell 的行为,shell 会拆分读取的所有内容stdin
,并尝试将每个单词放入相应的变量中,如果单词数超过变量数 - 它会尝试将所有内容推入最后一个变量。我建议您一次读取一个文件。
3. 复制到自身是错误的
你正在做的
cp $file1 $file1;
这将产生一个错误
$ cp input.txt input.txt
cp: 'input.txt' and 'input.txt' are the same file
也许你想做
cp "$file1" "$file1".tmp
或者像我一样直接使用mktemp
命令。还要注意变量的引用,以防止分词。
其他有趣的方法
你知道吗cat 任何文件使用重定向来制作副本?所以使用mv
或cp
不是唯一的方法。像这样:
$ cat ./wallpaper.jpg > wallpaper.jpg.tmp
$ cat ./screenshot.jpg > wallpaper.jpg
$ cat ./wallpaper.jpg.tmp > ./screenshot.jpg
答案2
如果获取两个文件名,您可以使用参数扩展来完成任务,或者您可以在脚本中读取它们。我的以下示例脚本使用参数扩展。您可能希望为移动选项使用临时目录,因为如果脚本中使用的文件名已经存在,则该文件将被默默覆盖。
#!/bin/bash
# creating a temporary directory to prevent overwrites if the file called 'tmpfile' exists
TMP=$(mktemp -d)
# copying the filenames into variables for later use
file1="$1"
file2="$2"
# swapping the files
# first move and rename file1 to $TMP/tempfile
mv "$1" $TMP/tempfile
# renaming file2 to file1 name
mv "$2" "$file1"
# now renaming and moving the tempfile to file2
mv $TMP/tempfile "$file2"
# removing the temporary folder if tempfile is not existent anymore
[ -e $TMP/tempfile ] && echo "Error!" || rm -r $TMP
参数是存储值的实体。它可以是名称、数字或下面列出的特殊字符之一。变量是用名称表示的参数。变量具有值和零个或多个属性。使用内建命令 declared 分配属性(请参阅 Bash Builtins 中内建命令 declared 的描述)。
如果已为参数分配了值,则表示已设置该参数。空字符串是有效值。一旦设置了变量,则只能使用 unset 内置命令取消设置。
可以通过以下形式的语句来分配变量
姓名=[价值]
如果你想从交互式对话框中读取文件名:
#!/bin/bash
# creating a temporary directory to prevent overwrites if the file called 'tmpfile' exists
TMP=$(mktemp -d)
# reading the filenames from user input into variables for later use
read -rp "Enter first filename: " file1
read -rp "Enter second filename: " file2
# swapping the files
# first move and rename file1 to $TMP/tempfile
mv "$file1" $TMP/tempfile
# renaming file2 to file1 name
mv "$file2" "$file1"
# now renaming and moving the tempfile to file2
mv $TMP/tempfile "$file2"
# removing the temporary folder if tempfile is not existent anymore
[ -e $TMP/tempfile ] && echo "Error!" || rm -r $TMP
read [-ers] [-a aname] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name …]
从标准输入或文件描述符读取一行自由度 作为选项的参数提供
-u
,按照上面 [单词拆分][6] 中的描述拆分成单词,第一个单词被分配给第一个姓名,第二个单词到第二个姓名等等。如果单词数多于名称数,则剩余的单词及其中间的分隔符将分配给最后一个姓名。如果从输入流中读取的单词数少于名称数,则将剩余的名称赋值为空值。变量值中的字符IFS
用于将行拆分为单词,使用与 shell 用于扩展的相同规则(如上文 [单词拆分][6] 中所述)。反斜杠字符 '\
' 可用于删除下一个读取字符和行延续的任何特殊含义。如果没有提供名称,则将读取的行分配给变量REPLY
。
read
接受多个选项。在本例中,两个选项最相关,因为您想向用户提问并获取他们的输入。这些选项是:
-r
→ 如果给出此选项,反斜杠将不充当转义字符。反斜杠被视为行的一部分。特别是,反斜杠-换行符对不能用作行延续。-p prompt
→ 显示迅速的,不带尾随换行符,然后尝试读取任何输入。仅当输入来自终端时才会显示提示。
虽然这不是最糟糕的情况-r
,但您几乎总是希望将其包括在内,以防止它\
充当转义字符。-p
向用户显示提示。