如何用分隔符拆分字符串并交换子字符串在bash中的位置?
How to split the string with delimiter and swap the substring's position in bash?
是这样的场景,
例如,我们有一个名为 a.txt、b.txt、...z.txt 的文件列表。目标是将它们命名为 txt.a,txt.b,txt.c.....txt.z
输入:
a.txt
b.txt
c.txt
...
...
z.txt
输出:
txt.a
txt.b
txt.c
...
...
txt.z
我尝试了这个单行并得到了想要的输出。
一行
ls -l *.txt | awk -F " " '{print }' | awk -F "." '{str1 = ; str2 = "."; str3 = ; str4 = str1 str2 str3; print str4}'
提问:
如果 bash 中还有更好的方法来实现此功能,有人可以提供帮助吗?
如果文件名是最后一列,并且文件只有一个点,您可以使用单个 awk 在一个点上拆分并反向打印两部分。
使用 $NF
匹配最后一个字段。
ls -l *.txt | awk '
{
split($NF, a, ".")
print a[2] "." a[1]
}'
如果拆分后应该有2个部分,可以查看拆分后的结果:
ls -l *.txt | awk '
{
n=split($NF, a, ".")
if (n == 2) print a[2] "." a[1]
}'
或者作为替代方法,使用 sed 和 2 个反向打印的捕获组,使用 ls -1
每行列出一个文件。
ls -1 *.txt | sed -E 's/^([^.]+)\.([^.]*)$/./'
Assumptions/understanding:
- objective 是重命名文件(问题提到
Goal is to name them as ...
但没有提到 mv
命令.. .???)
- 所有文件名都包含一个句点
- 我们想要切换文件名的 before-period/after-period 部分以创建新文件名
- 不存在具有新文件名的文件(例如,我们目前没有名为
a.txt
和 txt.a
的文件)
使用 parameter expansion 的一个想法可以让我们消除子进程调用的开销:
for fname in *.txt
do
new_fname="${fname#*.}.${fname%.*}"
echo mv "${fname}" "${new_fname}"
done
对于文件名 a.txt
、b.txt
和 c.txt
这会生成:
mv a.txt txt.a
mv b.txt txt.b
mv c.txt txt.c
一旦 OP 对结果感到满意,就可以删除 echo
并且脚本应该执行实际的 mv
/重命名。
如果基于 Perl 的 rename
命令可用,请尝试:
rename 's/^([^.]+)\.([^.]+)$/./' *.txt
重命名规则是Perl的替换表达式
使用正则表达式:
^
匹配(锚定)文件名的开头。
([^.]+)
匹配除点之外的任何字符的序列。
然后匹配的子串可以被</code>引用,因为周围有括号。</li>
<li><code>\.
匹配一个点。
再次 ([^.]+)
。现在由 </code>.</li> 引用
<li><code>$
匹配(锚定)文件名的末尾。
.
是替换部分,将两个子串互换
点对点。
请注意有两个不同的命令名为 rename
。一个是
基于Perl,其他不是。您可以通过以下方式识别它
看到联机帮助页。如果它包含单词 perlexpr
,则它是基于 Perl 的。
for f in [a-z].txt; do mv "$f" "txt.${f%.*}"; done
是这样的场景,
例如,我们有一个名为 a.txt、b.txt、...z.txt 的文件列表。目标是将它们命名为 txt.a,txt.b,txt.c.....txt.z
输入:
a.txt
b.txt
c.txt
...
...
z.txt
输出:
txt.a
txt.b
txt.c
...
...
txt.z
我尝试了这个单行并得到了想要的输出。 一行
ls -l *.txt | awk -F " " '{print }' | awk -F "." '{str1 = ; str2 = "."; str3 = ; str4 = str1 str2 str3; print str4}'
提问: 如果 bash 中还有更好的方法来实现此功能,有人可以提供帮助吗?
如果文件名是最后一列,并且文件只有一个点,您可以使用单个 awk 在一个点上拆分并反向打印两部分。
使用 $NF
匹配最后一个字段。
ls -l *.txt | awk '
{
split($NF, a, ".")
print a[2] "." a[1]
}'
如果拆分后应该有2个部分,可以查看拆分后的结果:
ls -l *.txt | awk '
{
n=split($NF, a, ".")
if (n == 2) print a[2] "." a[1]
}'
或者作为替代方法,使用 sed 和 2 个反向打印的捕获组,使用 ls -1
每行列出一个文件。
ls -1 *.txt | sed -E 's/^([^.]+)\.([^.]*)$/./'
Assumptions/understanding:
- objective 是重命名文件(问题提到
Goal is to name them as ...
但没有提到mv
命令.. .???) - 所有文件名都包含一个句点
- 我们想要切换文件名的 before-period/after-period 部分以创建新文件名
- 不存在具有新文件名的文件(例如,我们目前没有名为
a.txt
和txt.a
的文件)
使用 parameter expansion 的一个想法可以让我们消除子进程调用的开销:
for fname in *.txt
do
new_fname="${fname#*.}.${fname%.*}"
echo mv "${fname}" "${new_fname}"
done
对于文件名 a.txt
、b.txt
和 c.txt
这会生成:
mv a.txt txt.a
mv b.txt txt.b
mv c.txt txt.c
一旦 OP 对结果感到满意,就可以删除 echo
并且脚本应该执行实际的 mv
/重命名。
如果基于 Perl 的 rename
命令可用,请尝试:
rename 's/^([^.]+)\.([^.]+)$/./' *.txt
重命名规则是Perl的替换表达式 使用正则表达式:
^
匹配(锚定)文件名的开头。([^.]+)
匹配除点之外的任何字符的序列。 然后匹配的子串可以被</code>引用,因为周围有括号。</li> <li><code>\.
匹配一个点。
再次 ([^.]+)
。现在由</code>.</li> 引用 <li><code>$
匹配(锚定)文件名的末尾。.
是替换部分,将两个子串互换 点对点。
请注意有两个不同的命令名为 rename
。一个是
基于Perl,其他不是。您可以通过以下方式识别它
看到联机帮助页。如果它包含单词 perlexpr
,则它是基于 Perl 的。
for f in [a-z].txt; do mv "$f" "txt.${f%.*}"; done