Использование sed с пользовательской функцией не удается

У меня есть огромный CSV-файл с кодировкой URL.

Я хотел бы расшифровать все строки, и я подумал, что sed может помочь мне, но я не могу заставить его работать.

Вот мой сценарий:

#!/bin/bash

function urldecode() {
    # urldecode <string>
    # from https://gist.github.com/cdown/1163649

    local url_encoded="${1//+/ }"
    printf '%b' "${url_encoded//%/\\x}"
}

export -f urldecode

sed -e 's/.*/urldecode &/e' big_file.csv

Это приводит к повторяющемуся сообщению об ошибке sh: 1: urldecode: not found

EDIT Почему-то это работает в одной оболочке, но не в другой. Он работает в Git Bash в Windows, но не в Ubuntu 18.04 в Windows. Оба работают под управлением GNU bash 4.4.19, но, очевидно, они немного отличаются друг от друга.

2
задан 21 October 2019 в 15:00

1 ответ

Как @steeldriver указанный, sed будет метать икру /bin/sh который является symlinked к /bin/dash в текущих релизах Ubuntu и не поддерживает функции. Это вызвано тем, что sed внутренне использование popen, который всегда мечет икру /bin/sh (см. человека popen).

Если Вы не можете или не хотеть делать удар оболочкой по умолчанию и потребностью использовать функции удара в sed, можно использовать следующее обходное решение.

Для создания /bin/sh точка к /bin/bash, мы сначала используем недолю для порождения нового удара с частным пространством имен монтирования, bindmount /bin/bash на /bin/dash и затем выполните команду sed:

unshare -m -r bash -c "mount --bind /bin/bash /bin/dash && sed -e 's/.*/urldecode &/e' big_file.csv"

Тем путем все экспортируемые функции сохраняются. Можно также записать функцию, таким образом, Вы не должны писать целой недоле... часть все время, например:

#!/bin/bash

function mysed() {
    sedcommand=sed
    # restore quotes around each script
    while test $# -gt 0; do
        [[ "$1" == "-e" ]] && { shift; sedcommand="$sedcommand -e '$1'"; } || sedcommand="$sedcommand $1"; shift
    done
    unshare -m -r bash -c "mount --bind /bin/bash /bin/dash && $sedcommand"
}

function urldecode() {
    local url_encoded="${1//+/ }"
    printf '%b' "${url_encoded//%/\\x}"
}

export -f urldecode

mysed -e 's|.*|urldecode &|e' big_file.csv

Знайте, тем не менее, что -r опция unshare, то, которое необходимо для сможения к bindmount, создает своего рода виртуальную среду, в которой Вы - корень. Полномочия чтения-записи совпадают с пользователем, который звонил unshare, но uid и ценуроз будут 0. Например, если Вы звоните whoami внутри urldecode, это распечатает root.

Вы могли также просто запустить целый скрипт с помощью недоли:

unshare -m -r bash -c "mount --bind /bin/bash /bin/dash && ./script.sh"

... но ограничения из предыдущего абзаца применяются.

1
ответ дан 2 December 2019 в 04:35

Другие вопросы по тегам:

Похожие вопросы: