Может ли расширение переменной Bash выполняться непосредственно при вводе пользователем?

Решение Python:

import re,sys;  
for l in sys.stdin:
   if 'ADD' in l:
       data = [re.split(",",l.rstrip("\n"))[0].split()[-1]]+re.split(",",l.rstrip("\n"))[1:] 
       print " ".join(data)
       data = []

Протестировано с пользовательским файлом на основе примера OP:

$ cat input.txt                                                                                                                       
cop1010 ADD atra522,Allison Track,CT,canada
some other stuff
testuser ADD test522,Allison TEST,CT,germany
$ python user_data.py < input.txt                                                                                                     
atra522 Allison Track CT canada
test522 Allison TEST CT germany

Для совместимости с python 3 и хорошей печати данных мы могли бы сделать что-то вроде этого:

$ cat user_data.py                                                                                                                    
from __future__ import print_function
import re,sys;  
for l in sys.stdin:
   if 'ADD' in l:
       data = [re.split(",",l.rstrip("\n"))[0].split()[-1]]+re.split(",",l.rstrip("\n"))[1:] 
       print("Username: " + data[0])
       print("Real Name: " + data[1])
       print("Some kind of two char string: "+ data[2])
       print("Location: " + data[3])
       print("- - -")
       data = []

$ python user_data.py < input.txt                                                                                                     
Username: atra522
Real Name: Allison Track
Some kind of two char string: CT
Location: canada
- - -
Username: test522
Real Name: Allison TEST
Some kind of two char string: CT
Location: germany
- - -
2
15.10.2016, 15:23
1 ответ

Наивный способ был бы следующим:

eval "dirin=$dirin"

Это оценивает расширение dirin = $ dirin как шелл-кода.

С дирин , содержащим ~ / foo , он фактически вычисляет:

dirin=~/foo

Легко увидеть ограничения. С dirin , содержащим foo bar , это становится:

dirin=foo bar

Итак, он работает bar с dirin = foo в его среде (и у вас будут другие проблемы со всеми специальными символами оболочки).

Здесь вам нужно решить, какие расширения разрешены (тильда, подстановка команд, расширение параметров, подстановка процесса, арифметическое расширение, расширение имени файла ...), и либо выполнить эти замены вручную, либо использовать eval но экранируют все символы, кроме тех, которые позволяют им, что было бы практически невозможно, кроме как путем реализации полного синтаксического анализатора оболочки, если вы не ограничите его, например, ~ foo , $ VAR , $ {VAR} .

Здесь я бы использовал zsh вместо bash , у которого есть специальный оператор для этого:

vared -cp "input> " dirin
printf "%s\n" "${(e)dirin}"

vared - это редактор переменных , аналогично bash read -e .

(e) - это флаг расширения параметра, который выполняет раскрытие (параметр, команда, арифметика, но не тильда) в содержимом параметра.

Чтобы обратиться к раскрытию тильды, которое имеет место только в начале строки, мы должны сделать:

vared -cp "input> " dirin
if [[ $dirin =~ '^(~[[:alnum:]_.-]*(/|$))(.*)' ]]; then
  eval "dirin=$match[1]\${(e)match[3]}"
else
  dirin=${(e)dirin}
fi

POSIXly (так bash ly), чтобы выполнить тильду и переменную (не параметр ), вы можете написать такую ​​функцию, как:

expand_var() {
  eval "_ev_var=\${$1}"
  _ev_outvar=
  _ev_v=${_ev_var%%/*}
  case $_ev_v in
    (?*[![:alnum:]._-]*) ;;
    ("~"*)
      eval "_ev_outvar=$_ev_v"; _ev_var=${_ev_var#"$_ev_v"}
  esac

  while :; do
    case $_ev_var in
      (*'$'*)
        _ev_outvar=$_ev_outvar${_ev_var%%"$"*}
        _ev_var=${_ev_var#*"$"}
        case $_ev_var in
          ('{'*'}'*)
            _ev_v=${_ev_var%%\}*}
            _ev_v=${_ev_v#"{"}
            case $_ev_v in
              "" | [![:alpha:]_]* | *[![:alnum:]_]*) _ev_outvar=$_ev_outvar\$ ;;
              (*) eval "_ev_outvar=\$_ev_outvar\${$_ev_v}"; _ev_var=${_ev_var#*\}};;
            esac;;
          ([[:alpha:]_]*)
            _ev_v=${_ev_var%%[![:alnum:]_]*}
            eval "_ev_outvar=\$_ev_outvar\$$_ev_v"
            _ev_var=${_ev_var#"$_ev_v"};;
          (*)
            _ev_outvar=$_ev_outvar\$
        esac;;
      (*)
        _ev_outvar=$_ev_outvar$_ev_var
        break
    esac
  done
  eval "$1=\$_ev_outvar"
}

Пример:

$ var='~mail/$USER'
$ expand_var var;
$ printf '%s\n' "$var"
/var/mail/stephane

В качестве приближения мы могли бы также добавить каждый символ, кроме ~ $ {} -_. и alnums с обратной косой чертой перед переходом к eval :

eval "dirin=$(
  printf '%s\n' "$dirin" |
    sed 's/[^[:alnum:]~${}_.-]/\\&/g')"

(здесь упрощено на том основании, что $ dirin не может содержать символы новой строки, поскольку это происходит из читать )

Это вызовет синтаксические ошибки, если ввести $ {foo # bar} , например, но, по крайней мере, это не принесет большого вреда, как простой eval . .

Изменить : рабочим решением для bash и других оболочек POSIX было бы разделение тильды и других расширений, как в zsh , и использование eval с здесь-документом для других расширений , например:

expand_var() {
  eval "_ev_var=\${$1}"
  _ev_outvar=
  _ev_v=${_ev_var%%/*}
  case $_ev_v in
    (?*[![:alnum:]._-]*) ;;
    ("~"*)
      eval "_ev_outvar=$_ev_v"; _ev_var=${_ev_var#"$_ev_v"}
  esac
  eval "$1=\$_ev_outvar\$(cat << //unlikely//
$_ev_var
//unlikely//
)"

Это позволит расширять тильду, параметры, арифметические операции и команды, как в zsh выше. }

5
27.01.2020, 21:56

Теги

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