在 shell 脚本中将一个子字符串替换为另一个字符串

问题描述 投票:0回答:17

我有“我爱苏子结婚”,我想把“苏子”改成“莎拉”

firstString="I love Suzi and Marry"
secondString="Sara"

期望的结果:

firstString="I love Sara and Marry"
bash shell
17个回答
2134
投票

要用给定的字符串替换 first 出现的模式,请使用

${parameter/pattern/string}
:

#!/bin/bash
firstString="I love Suzi and Marry"
secondString="Sara"
echo "${firstString/Suzi/"$secondString"}"    
# prints 'I love Sara and Marry'

要替换 all 出现,请使用

${parameter//pattern/string}
:

message='The secret code is 12345'
echo "${message//[0-9]/X}"           
# prints 'The secret code is XXXXX'

(这记录在Bash 参考手册,§3.5.3“Shell 参数扩展”。)

注意这个特性不是由 POSIX 指定的——它是一个 Bash 扩展——所以并不是所有的 Unix shell 都实现它。对于相关的 POSIX 文档,请参阅 The Open Group Technical Standard Base Specifications,第 7 期Shell & Utilities 卷,§2.6.2“参数扩展”


317
投票

这完全可以通过 bash 字符串操作来完成:

first="I love Suzy and Mary"
second="Sara"
first=${first/Suzy/$second}

这将只替换第一次出现的;要全部替换它们,请将第一个斜杠加倍:

first="Suzy, Suzy, Suzy"
second="Sara"
first=${first//Suzy/$second}
# first is now "Sara, Sara, Sara"

208
投票

对于 Dash,所有以前的帖子都不起作用

POSIX

sh
兼容解决方案是:

result=$(echo "$firstString" | sed "s/Suzi/$secondString/")

这将替换每行输入中的第一次出现。添加一个

/g
标志来替换所有出现的地方:

result=$(echo "$firstString" | sed "s/Suzi/$secondString/g")

84
投票

试试这个:

 sed "s/Suzi/$secondString/g" <<<"$firstString"

68
投票

如果字符串有正则表达式字符,使用 Bash 比

sed
更好。

echo ${first_string/Suzi/$second_string}

它可以移植到 Windows,并且至少可以与 Bash 3.1 一样老。

为了表明你不需要太担心逃跑,让我们把这个:

/home/name/foo/bar

进入这个:

~/foo/bar

但前提是

/home/name
在开头。我们不需要
sed

鉴于 Bash 为我们提供了魔法变量

$PWD
$HOME
,我们可以:

echo "${PWD/#$HOME/\~}"

感谢 Mark Haferkamp 在评论中对引用/转义的说明

~
。*

注意变量

$HOME
是如何包含斜杠的,但这并没有破坏任何东西。

进一步阅读:高级 Bash 脚本指南
如果必须使用

sed
,请务必转义每个字符


33
投票
echo [string] | sed "s|[original]|[target]|g"
  • “s”表示“替代”
  • “g”表示“全局的,所有匹配的事件”

31
投票

如果明天你决定不爱Marry either她也可以被替换:

today=$(</tmp/lovers.txt)
tomorrow="${today//Suzi/Sara}"
echo "${tomorrow//Marry/Jesica}" > /tmp/lovers.txt

必须有50种离开爱人的方式.


19
投票

因为我不能添加评论。 @ruaka 为了使示例更具可读性,可以这样写

full_string="I love Suzy and Mary"
search_string="Suzy"
replace_string="Sara"
my_string=${full_string/$search_string/$replace_string}
or
my_string=${full_string/Suzy/Sarah}

13
投票

使用AWK

firstString="I love Suzi and Marry"
echo "$firstString" | awk '{gsub("Suzi","Sara"); print}'

9
投票

Pure POSIX shell 方法,不同于 Roman Kazanovskyi

sed
-based answer不需要外部工具,只需要 shell 自己的本地参数扩展。请注意,长文件名已被最小化,因此代码更适合放在一行中:

f="I love Suzi and Marry"
s=Sara
t=Suzi
[ "${f%$t*}" != "$f" ] && f="${f%$t*}$s${f#*$t}"
echo "$f"

输出:

I love Sara and Marry

工作原理:

  • 删除最小后缀模式。如果后缀

    "${f%$t*}"
    I love
    ”在
    $t
    Suzi*
    $f
    ”中,I love返回“
    Suzi and Marry
    ”。

  • 但是如果

    t=Zelda
    ,那么
    "${f%$t*}"
    什么都不删除,并返回整个字符串“
    I love Suzi and Marry
    ”。

  • 这用于测试

    $t
    是否在
    $f
    [ "${f%$t*}" != "$f" ]
    中,如果$f字符串包含“
    Suzi*
    ”,则评估为
    true
    ,否则为false

  • 如果测试返回 true,使用 Remove Smallest Suffix Pattern

    ${f%$t*}
    "
    I love
    " 和 Remove Smallest Prefix Pattern
    ${f#*$t}
    "
    and Marry
    " 构造所需的字符串,其中第二个字符串
    $s
    Sara
    ”在两者之间。


5
投票

用特殊章程替换第一次出现的模式:

${参数/模式/字符串}

用特殊章程替换所有事件的模式:

${参数//模式/字符串}

firstString="I love //Suzi// and Marry"
secondString="Sara"
firstString="${firstString/\/\/Suzi\/\//"$secondString"}"
echo $firstString

它将打印:

I love Sara and Marry


3
投票

我认为这是您的用例最简洁的形式:

firstString="${firstString//Suzi/$secondString}"

0
投票

试试这个:

ls *.ext | awk '{print "mv "$1" "$1".newext"}' | sed "s/.ext.newext/.newext/" | parallel {}

0
投票

基于上面提出的 awk 解决方案,我将扩展它以使用 awk 变量。 这将允许传递包含特殊字符的文本..

aString="I love _p1_ very much!"
aVar="complicated \" text \' with \. special ) chars"
awk -v p1="$aVar" '{gsub("_p1_",p1); print}' <<< $aString

出品:

I love complicated " text ' with . special ) chars very much

sed -e
bash
替换来实现这个案例是不容易的。


-1
投票

我发现的唯一方法是将字符串存储在文件中,使用 sed 然后将文件内容存储在 var 中:

echo "I love Suzy" > tmp.txt
sed -i "s/Suzy/Sarah/" tmp.txt
set res=`cat tmp.txt`
echo $res
rm tmp.txt

我不知道我使用的是哪种 shell(如果我键入“sh”,我发现的唯一东西是 sh-4.2)但是所有经典语法都失败了,比如简单的

test=${test2}
。 它失败了 2 次:在分配(必须使用
set
)和在
${}
.


-1
投票

使用

sed
我们可以轻松做到

sed -i "s+$value_to_be_replaced+$with_variable1 "some character" $with_variable2+g" $file_name

-2
投票

删除早于 period 的 git 分支

这是一个自动 bash 脚本,用于删除早于特定时期的 git 分支。默认设置时间为 3 个月,但您可以在运行 shell 脚本时将以月为单位的时间作为第一个参数传递。


#!/bin/sh

declare -i numberOfMonths=3 # Declare the default period in months

# Check for passed period(In months) parameter
if [ -z "$1" ]; 
then
  # Period not set
  echo "Period not set. Assuming delete period to the default $numberOfMonths months period!"
  echo ""
  sleep 2; # Hold for 2 seconds
else
  
    # Period set
  numberOfMonths=$1; # Set the passed period
fi

echo "Deleting branches older than $numberOfMonths months"
echo ""
sleep 1 # Hold for a second

ECHO='echo ' # Custom echo

: '
  Initiate loop scanning for branches older than the passed time or set default while excluding the below branches
  main, master
'
for target_branch in $(git branch -a | sed 's/^\s*//' | sed 's/^remotes\///' | grep -v 'main$\|master$'); do
  # Check period
  if ! ( [[ -f "$target_branch" ]] || [[ -d "$target_branch" ]] ) && [[ "$(git log $target_branch --since "$numberOfMonths month ago" | wc -l)" -eq 0 ]]; then
    if [[ "$DRY_RUN" = "false" ]]; then
        ECHO="" # Print empty
    fi

    local_target_branch_name=$(echo "$target_branch" | sed 's/remotes\/origin\///') # Get target branch in iteration
    local_target_branch_name=${local_target_branch_name/origin\//} # Replace string "origin/" with empty(string)
    $ECHO Deleting Local Branch : "${local_target_branch_name}" # Print message
    sleep 1 # Hold for a second
    git branch -d "${local_target_branch_name}" # Delete local branch
    $ECHO Deleting Remote Branch : "${local_target_branch_name}" # Print message
    sleep 1 # Hold for a second
    git push origin --delete "${local_target_branch_name}" # Delete remote branch
  fi
done
© www.soinside.com 2019 - 2024. All rights reserved.