linux sed i参数,sed 慎用 -i 参数

sed (Stream Editor, 文本流编辑器) 可谓是 shell 中必不可少一款文本处理工具了。我们经常会使用 sed -i 参数来对一些配置文件做自动化修改,但是此次在 docker 这样做中却遇到了问题sed: can't move '/etc/redis.confDhPCho' to '/etc/redis.conf': Resource busy

docker-entrypoint.sh 启动部分如下,# first boot

if [ ! -f /data/dump.rdb ]; then

if [ -z "$REDIS_PASSWORD" ]; then

REDIS_PASSWORD="$(pwgen 16 1)"

echo "[INFO] Generated Redis Password: $REDIS_PASSWORD"

fi

sed -i "s/^# requirepass foobared$/requirepass $REDIS_PASSWORD/" /etc/redis.conf

fi

docker-compose.yml 部分配置文件如下,redis:

build: ./redis

image: docker-lnmp-redis:latest

ports:

- "6379:6379"

networks:

- backend

volumes:

- ./redis/redis.conf:/etc/redis.conf:rw

- ./data/logs/redis/:/var/log/redis/:rw

- ./data/redis/:/data/:rw

environment:

REDIS_PASSWORD: 123456

restart: always

container_name: redis

根据启动文件可知由该镜像创建的容器首次启动会对 /etc/redis.conf 文件做修改,而在 docker-compose.yml 中配置外部文件映射到容器内 /etc/redis.conf,也即容器首次启动就会修改这个外部文件。

然而 sed 并不给面子,抛出了一个异常,究其原因,在于 sed -i 此操作看似是修改了文件,但实际上为使用具有修改后的内容的新文件覆盖了原文件,这将导致文件的 Inode 发生变化,而在 docker 中挂载的文件是不允许这样做的。类似的问题还存在于 sed -i 修改符号文件。$ echo 123 > txt

$ stat txt

File: txt

Size: 4               Blocks: 8          IO Block: 4096   regular file

Device: fc01h/64513d    Inode: 1179795     Links: 1

Access: (0664/-rw-rw-r--)  Uid: ( 1000/    choi)   Gid: ( 1000/    choi)

Access: 2019-03-09 03:11:32.293005107 +0800

Modify: 2019-03-09 03:11:32.293005107 +0800

Change: 2019-03-09 03:11:32.293005107 +0800

Birth: -

# 使用 sed -i 修改文件 Inode 会变

$ sed -i 's/123/abc/' txt

$ stat txt

File: txt

Size: 4               Blocks: 8          IO Block: 4096   regular file

Device: fc01h/64513d    Inode: 1179862     Links: 1

Access: (0664/-rw-rw-r--)  Uid: ( 1000/    choi)   Gid: ( 1000/    choi)

Access: 2019-03-09 03:12:42.571444788 +0800

Modify: 2019-03-09 03:12:42.571444788 +0800

Change: 2019-03-09 03:12:42.571444788 +0800

Birth: -

为了避免文件覆盖导致 Inode 变化,于是乎有了一个大胆的想法——使用重定向来重写文件内容$ echo 123 > txt

$ stat txt

File: txt

Size: 4               Blocks: 8          IO Block: 4096   regular file

Device: fc01h/64513d    Inode: 1179795     Links: 1

Access: (0664/-rw-rw-r--)  Uid: ( 1000/    choi)   Gid: ( 1000/    choi)

Access: 2019-03-09 03:14:10.738505467 +0800

Modify: 2019-03-09 03:14:10.738505467 +0800

Change: 2019-03-09 03:14:10.738505467 +0800

Birth: -

# 使用重定向修改不会导致文件 Inode 变化

$ echo abc > txt

$ stat txt

File: txt

Size: 4               Blocks: 8          IO Block: 4096   regular file

Device: fc01h/64513d    Inode: 1179795     Links: 1

Access: (0664/-rw-rw-r--)  Uid: ( 1000/    choi)   Gid: ( 1000/    choi)

Access: 2019-03-09 03:14:10.738505467 +0800

Modify: 2019-03-09 03:15:19.244883632 +0800

Change: 2019-03-09 03:15:19.244883632 +0800

Birth: -

接着有了如下修改......sed "s/^# requirepass foobared$/requirepass $REDIS_PASSWORD/" /etc/redis.conf > /etc/redis.conf

然鹅,正当沉浸在找到问题原因所在的欢乐中,现实再一次给了沉痛一击......

是的,Inode 是没变,但是文件内容已经空空如也了!!!

为什么会这样呢,原因在于 I/O 重定向时 stdout 与 stderr 的管道会先准备好再从 stdin 读入,也就是说 > file 时会先将 file 清空然后再读入数据,因此不等到 sed 读数据时文件已经就是空的了。# I/O 重定向会先清空文件

$ echo 123 > txt

$ cat  txt

$ cat test.txt

正确做法如下,既保证了 Inode 不变又修改了文件内容$ echo 123 > txt

# 注意不可以再将 tee txt >/dev/null 否则又是空

# https://github.com/koalaman/shellcheck/wiki/SC2094

$ cat txt | sed 's/123/abc/' | tee txt

# 或

$ sed 's/123/abc' txt > tmp && cat tmp > txt && rm tmp

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值