使用sed将首字母转换为小写

3

我正在运行一个基于Alpine的Docker镜像,其中安装了bash。

bash --version
GNU bash, version 4.3.48(1)-release (x86_64-alpine-linux-musl)

在以下命令中,我试图将月份的第一个字母转换为小写,但它没有起作用:
find ../ -type f -name "*.md" \
        -exec bash -c '
            f=${1#./}
            gzip -9 "$f"
            mv "$f".gz "$f"
            aws s3 cp "$f" s3://bucket_name/ --metadata time=$(date +%d-%b-%Y.%H%M | sed '\''s#\([A-Z]\)#\L\1#'\'') ' _ {} \;

它分配给文件的日期属性是:
"Metadata": {
    "timestamp": "10-LSep-2018.1054"
}

\L在这种情况下无法工作。预期日期应该是"10-sep-2018.1054"

如何在Docker镜像中的bash版本中使其工作?


1
你声称 \L 不起作用,但是你的示例实际上显示了一个未转义的 L。如果要将任何单词的首字母转换为小写,请尝试使用 sed -E "s/\b[^A-Z]*[A-Z]/\L&/" - Sam
@Sam:非常抱歉那个打字错误。实际上应该是\L。 - Technext
1
嗯,对我来说可以运行,也许你使用的是不同版本的sed?另外,也许一个简单的tr [:upper:] [:lower:]就足够满足你的需求了。 - Sam
我刚刚检查了版本,它显示“这不是GNU sed 4.0版本”。这个似乎是情况。无论如何,你的第一个建议不起作用,可能是因为我刚刚提到的原因。我得到的输出是:“L10-Sep-2018.1142”。第二个建议确实有效。 - Technext
2
如果不关心其他大写字母是否需要保留,我会选择使用 tr 来提高可读性。 - Sam
@Sam:请将您的建议作为答案发布。我肯定会等待一段时间来获取一个sed的答案,但如果没有其他答案出现,我会接受您的建议。尽管它在应用范围上很广泛,但对于我的使用情况来说,我觉得使用它是安全且易读的。 - Technext
5个回答

3
事实证明,OP只能访问到一个最简版本的sed,它不识别\L替换命令,并且在他的用例中,将输出中的所有字母都转为小写是可以接受的。
实现这一目标最直接的方法是使用以下命令:
tr [:upper:] [:lower:]

1
这个问题可以简化为只显示受影响的部分:
date +%d-%b-%Y.%H%M | sed '\''s#\([A-Z]\)#L\1#'\''

首先让我“清理”一下它:
date +%d-%b-%Y.%H%M | sed 's#\([A-Z]\)#L\1#'

我看到你在使用L作为字面量而不是命令。你需要像这样转义它(\L):

date +%d-%b-%Y.%H%M | sed 's#\([A-Z]\)#\L\1#'

你可以执行以下测试:
:~# docker container run --rm alpine /bin/sh
/ # date +%d-%b-%Y.%H%M | sed 's#\([A-Z]\)#\L\1#'
10-LSep-2018.1510

谢谢回复,但我没有尝试简化,因为我正在使用我发布的原始版本(整个命令)。尝试了你的建议,但是出现了错误:-c: line 4: unexpected EOF while looking for matching )'`。 - Technext
如果我运行你的Docker命令,它会以正确的格式打印日期。然而,如果我使用相同的Alpine容器运行这个命令docker container run --rm alpine sed --version,输出结果是This is not GNU sed version 4.0。这正是当我在我的脚本中打印sed --version的输出时得到的结果。根据你和我的sed版本细节,我推测\L在我的镜像中也可能被支持,但在使用方式上需要进行一些调整。 - Technext
所以我刚刚确认我的 sed 版本也支持 \L。我只是在你建议的命令中使用了我的容器来代替你的,现在它可以正确地打印日期了。正如我之前所说,原始命令需要进行一些调整。 - Technext
我认为这与转义级别有关。Bash中的转义(\ L)可能在sed转义之前起作用(只会得到L)。你能尝试双重转义\L吗?像这样:sed 's#([A-Z])#\L\1#' - binary-sequence
1
啊,抱歉,我的错。我发送的Docker命令是错误的。管道命令不是在容器内执行,而是在外部执行。只有日期命令在容器内执行,然后将输出发送到主机,主机再执行sed命令。我已经验证了alpine镜像中的sed命令没有\L命令。 - binary-sequence
显示剩余3条评论

1
这个 GNU sed 应该可以做到:
$ date +'%d-%b-%Y.%H%M' | sed 's/-./\L&/'
10-sep-2018.1332

bash -c '...' 中正确转义:

bash -c '... time="$(date +"%d-%b-%Y.%H%M" | sed "s/-./\L&/")" ...'

如果您没有GNU sed\L扩展需要它)可用,只需将日期保存到变量中并使用参数扩展即可:
$ d=$(date +'%d-%b-%Y.%H%M')
$ echo "${d,,}"
10-sep-2018.1337

谢谢你告诉我GNU sed支持\L。对此给予加分。我见过第二种方法,但我想避免单独处理日期。如果我得不到一个sed的答案,我可能会选择@Sam建议的tr方法。 - Technext
使用您最新的建议,我将目录名设置为10L-Sep-2018.1540 - Technext

1
这可能适用于你(GNU sed):
sed 's/$/:JjFfMmAaSsOoNnDd/;s/\([JFMASOND]\)\(.*\):.*\1\(.\).*/\3\2/' file

这将大写字母J、F、M、A、S、O、N或D替换为其小写补充。


我尝试过了,但是出现了find: -exec requires an argument的错误。 - Technext

0

在这个其他的讨论中,有几种可能的答案变体,但似乎满足你需求并且最容易理解的是以下这个:

sed 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'

这应该在几乎所有地方都能正常工作,包括 macOS。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接