bash shell 脚本中的连接和“basename”的使用
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 
原文地址: http://stackoverflow.com/questions/13737053/
Warning: these are provided under cc-by-sa 4.0 license.  You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Concatenation in shell script and use of 'basename'
提问by Pranav
I want to read all file names form a particular directory and then create new files with those names by appending some string to them in another directory.
我想从特定目录中读取所有文件名,然后通过在另一个目录中向它们附加一些字符串来创建具有这些名称的新文件。
e.g > 'A', 'B', 'C' are in 'logs' directory then script should create 'A_tmp', 'B_tmp', 'C_tmp' in 'tmp' directory
例如 > 'A'、'B'、'C' 在 'logs' 目录中然后脚本应该在 'tmp' 目录中创建 'A_tmp'、'B_tmp'、'C_tmp'
what i am using is -
我正在使用的是 -
tempDir=./tmp/
logDir=./logs/
for file in $( find `echo $logDir` -type f )
 do
      name=eval basename $file
      echo $name
      name=$(echo $name | sed 's/.$//')
      echo $tempDir
      opFile=$tempDir$name
      echo $opFile
 done
But what I understood is, $file is containing '\n' as last character and I am unable to concatenate the string.
但我的理解是, $file 包含 '\n' 作为最后一个字符,我无法连接字符串。
right now I am not creating files, just printing all the names.
现在我没有创建文件,只是打印所有名称。
So, how I can remove the '\n' from the file name, and is my understanding correct ?
那么,如何从文件名中删除“\n”,我的理解是否正确?
回答by Jonathan Leffler
Analysis
分析
There are multiple issues to address in your script. Let's take it step by step:
您的脚本中有多个问题需要解决。让我们一步一步来:
tempDir=./tmp/
logDir=./logs/
for file in $( find `echo $logDir` -type f )
This scheme assumes no spaces in the file names (which is not an unusual restriction; avoiding problems with spaces in names is relatively tricky).  Also, there's no need for the echo; just write:
该方案假定文件名中没有空格(这不是一个不寻常的限制;避免名称中的空格问题相对棘手)。此外,也不需要echo; 写就好了:
for file in $(find "$logDir" -type f)
Continuing:
继续:
do
    name=eval basename $file
This runs the basenamecommand with the environment variable nameset to the value evaland the argument $file.  What you need here is:
这将在basename环境变量name设置为 valueeval和参数的情况下运行命令$file。你需要的是:
    name=$(basename "$file")
where the double quotes aren't strictly necessary because the name can't contain spaces (but it's not a bad habit to get into to quote all file names because sometimes the names do contain spaces).
其中双引号不是绝对必要的,因为名称不能包含空格(但引用所有文件名并不是一个坏习惯,因为有时名称确实包含空格)。
    echo $name
This would echo a blank line because name was not set.
这将回显一个空行,因为未设置名称。
    name=$(echo $name | sed 's/.$//')
If name was set, this would chop off the last character, but if the name was A, you'd have nothing left.
如果设置了 name,这将切断最后一个字符,但如果 name 是A,则您将一无所有。
    echo $tempDir
    opFile=$tempDir$name
    echo $opFile
done
Give or take double quotes and the fact that you've not added the _tmpsuffix to opFile, there's nothing wrong with the rest.
给出或采用双引号以及您没有将_tmp后缀添加到 的事实,opFile其余的没有任何问题。
Synthesis
合成
Putting the changes together, you end up with:
将更改放在一起,您最终会得到:
tempDir=./tmp/
logDir=./logs/
for file in $(find "$logDir" -type f)
do
    name=$(basename "$file")
    echo "$name"                    # Debug only
    echo "$tempDir"                 # Debug only
    opFile="$tempDir${name}_tmp"
    echo "$opFile"
done
That shows all the intermediate results. You could perfectly well compress that down to:
这显示了所有的中间结果。您完全可以将其压缩为:
tempDir=./tmp/
logDir=./logs/
for file in $(find "$logDir" -type f)
do
    opFile="$tempDir"$(basename "$file")"_tmp"
    echo "$opFile"
done
Or, using a simpler combination of double quotes because the names contain no spaces:
或者,使用更简单的双引号组合,因为名称不包含空格:
tempDir=./tmp/
logDir=./logs/
for file in $(find "$logDir" -type f)
do
    opFile="$tempDir$(basename $file)_tmp"
    echo "$opFile"
done
The echo is there as a surrogate for the copy or move operation you plan to execute, of course.
当然,回声是您计划执行的复制或移动操作的替代品。
EDIT: ...and to remove restrictions on file names containing spaces and globbing characters, do it as:
编辑: ...并删除对包含空格和通配符的文件名的限制,请执行以下操作:
tempDir=./tmp/
logDir=./logs/
find "$logDir" -type f |
while IFS= read -r file
do
    opFile="${tempDir}${file##*/}_tmp"
    echo "$opFile"
done
It will still fail for file names containing newlines. If you want to handle that then investigate a solution using find ... -print0 | xargs -0or find ... -exec.
对于包含换行符的文件名,它仍然会失败。如果您想处理该问题,请使用find ... -print0 | xargs -0或调查解决方案find ... -exec。
回答by udoprog
Try the following.
请尝试以下操作。
#!/bin/sh
tmpDir=./tmp/
logDir=./logs/
# list all files in log directory, pipe into a loop that reads each path line
# by line..
# Also note that there is no newline in this case since it is swallowed by 'read'.
find $logDir -type f | while read path; do
    # get the basename of the path
    name=`basename $path`
    # copy the found file to the temporary directory.
    dest="$tmpDir/${name}_tmp"
    echo $dest
done
Shell scripts have the ability to concatenate strings easily in statements, as demonstrated with $tmpDir/${name}_tmp, there is no need for replacing the output since read swallows any newlines.
Shell 脚本能够在语句中轻松连接字符串,如$tmpDir/${name}_tmp 所示,无需替换输出,因为 read 吞下了任何换行符。
find ... while readis a very useful construct when you want to read multiple lines of anything, it even works for files.
find ...虽然当您想要读取任何内容的多行时read是一个非常有用的构造,它甚至适用于文件。
while read line; do
    echo $line
done < filename.txt
Edit: clarified
编辑:澄清
回答by Diego Basch
Try something like this:
尝试这样的事情:
tempDir=./tmp/
logDir=./logs/
for file in $( find `echo $logDir` -type f )
  do
    name=`eval basename $file|tr -d "\n"`_tmp
    echo $name
  done
回答by pbhd
If you change
如果你改变
name=eval basename $file
to
到
name=`eval basename $file`
then afterwads name contains what you want.
然后 afterwads 名称包含您想要的内容。

