Сценарий Bash на OSX не учитывает направление дома (~) [дубликат]

Простые значения внутри функций не изменят эти значения вне функции (они передаются по значению), тогда как сложные (будут переданы по ссылке).

function willNotChange(x) {

x = 1;

}

var x = 1000;

willNotChange(x);

document.write('After function call, x = ' + x + '<br>'); //still 1000

function willChange(y) {

y.num = 2;

}

var y = {num: 2000}; 

willChange(y);
document.write('After function call y.num = ' + y.num + '<br>'); //now 2, not 2000
101
задан madiyaan damha 18 October 2010 в 23:05
поделиться

13 ответов

Просто для продолжения ответа birryree для путей с пробелами: вы не можете использовать команду eval, так как она разделяет оценку пробелами. Одним из решений является временное замещение пробелов для команды eval:

mypath="~/a/b/c/Something With Spaces"
expandedpath=${mypath// /_spc_}    # replace spaces 
eval expandedpath=${expandedpath}  # put spaces back
expandedpath=${expandedpath//_spc_/ }
echo "$expandedpath"    # prints e.g. /Users/fred/a/b/c/Something With Spaces"
ls -lt "$expandedpath"  # outputs dir content

Этот пример, конечно, полагается на предположение, что mypath никогда не содержит последовательность символов "_spc_".

76
ответ дан Community 22 August 2018 в 10:51
поделиться
  • 1
    У вас есть исправление, когда переменная имеет место в нем? – Hugo 8 July 2011 в 14:01
  • 2
    Я нашел ${HOME} наиболее привлекательным. Есть ли причина, чтобы не сделать это основной рекомендацией? В любом случае, спасибо! – sage 5 September 2013 в 16:21
  • 3
    +1 - Мне нужно было расширять ~ $ some_other_user, и eval отлично работает, когда $ HOME не будет работать, потому что мне не нужен текущий пользовательский дом. – olivecoder 10 September 2013 в 12:30
  • 4
    Использование eval - ужасное предложение, это действительно плохо, что он получает так много upvotes. Вы столкнетесь со всеми проблемами, когда значение переменной содержит метасимволы оболочки. – user2719058 31 August 2014 в 20:47
  • 5
    Не работает с вкладками, новыми символами или чем-то еще в IFS ... и не обеспечивает безопасность вокруг метасимволов, таких как пути, содержащие $(rm -rf .) – Charles Duffy 13 April 2015 в 20:19
  • 6
    echo ${~root} не дает никакого результата на zsh (mac os x) – Orwellophile 11 June 2015 в 00:59
  • 7
    export test="~root/a b"; echo ${~test} – Gyscos 15 July 2015 в 17:49
  • 8
    Я не мог закончить свой комментарий в то время, и тогда мне не разрешили редактировать его позже. Поэтому я благодарен (спасибо снова @birryree) за это решение, поскольку оно помогло мне в конкретном контексте в то время. Спасибо, Чарльз, за ​​то, что он меня понял. – olivecoder 26 August 2015 в 15:42

Безопасный способ использования eval - "$(printf "~/%q" "$dangerous_path")". Обратите внимание, что это специфичный для bash.

#!/bin/bash

relativepath=a/b/c
eval homedir="$(printf "~/%q" "$relativepath")"
echo $homedir # prints home path

Подробнее см. этот вопрос

. Также обратите внимание, что в случае zsh это будет так же просто, как echo ${~dangerous_path}

76
ответ дан Community 22 August 2018 в 10:51
поделиться
  • 1
    У вас есть исправление, когда переменная имеет место в нем? – Hugo 8 July 2011 в 14:01
  • 2
    Я нашел ${HOME} наиболее привлекательным. Есть ли причина, чтобы не сделать это основной рекомендацией? В любом случае, спасибо! – sage 5 September 2013 в 16:21
  • 3
    +1 - Мне нужно было расширять ~ $ some_other_user, и eval отлично работает, когда $ HOME не будет работать, потому что мне не нужен текущий пользовательский дом. – olivecoder 10 September 2013 в 12:30
  • 4
    Использование eval - ужасное предложение, это действительно плохо, что он получает так много upvotes. Вы столкнетесь со всеми проблемами, когда значение переменной содержит метасимволы оболочки. – user2719058 31 August 2014 в 20:47
  • 5
    echo ${~root} не дает никакого результата на zsh (mac os x) – Orwellophile 11 June 2015 в 00:59
  • 6
    export test="~root/a b"; echo ${~test} – Gyscos 15 July 2015 в 17:49
  • 7
    Я не мог закончить свой комментарий в то время, и тогда мне не разрешили редактировать его позже. Поэтому я благодарен (спасибо снова @birryree) за это решение, поскольку оно помогло мне в конкретном контексте в то время. Спасибо, Чарльз, за ​​то, что он меня понял. – olivecoder 26 August 2015 в 15:42
[g4] Из-за природы StackOverflow, я не могу просто сделать этот ответ неприемлемым, но за прошедшие 5 лет с тех пор, как я его опубликовал, ответы были намного лучше, чем мой заведомо рудиментарный и довольно плохой ответ (я был молод, дон не убивай меня). [/g4] [g5] Другие решения в этой теме - более безопасные и лучшие решения. Предпочтительно, я бы пошел с любым из этих двух: [/g5] [g9] [g2] [g0] решение Даффи Чарльза [/g0] [/g2] [g3] [g1] решение Хокон Хегланда [/g1] [/g3] [/g9]
[g6] Оригинальный ответ для исторических целей (но, пожалуйста, не используйте это) [/g6] [g7] Если я не ошибаюсь, [f2] не будет расширяться скрипт bash таким образом, потому что он обрабатывается как литеральная строка [f3]. Вы можете принудительно выполнить расширение через [f4] следующим образом. [/G7] [f1] [g8] В качестве альтернативы, просто используйте [f5], если вы хотите домашний каталог пользователя. [/G8]
77
ответ дан Community 5 November 2018 в 08:27
поделиться
  • 1
    У вас есть решение, когда в переменной есть пробел? – Hugo 8 July 2011 в 14:01
  • 2
    Я нашел [f1] наиболее привлекательным. Есть ли причина не делать это вашей основной рекомендацией? В любом случае спасибо! – sage 5 September 2013 в 16:21
  • 3
    +1 - мне нужно было расширить ~ $ some_other_user, и eval отлично работает, когда $ HOME не будет работать, потому что мне не нужен текущий домашний пользователь. – olivecoder 10 September 2013 в 12:30
  • 4
    Использование [f1] - ужасное предложение, очень плохо, что он получает так много голосов. Вы столкнетесь со всевозможными проблемами, когда значение переменной содержит метасимволы оболочки. – user2719058 31 August 2014 в 20:47
  • 5
    Я не мог закончить свой комментарий в то время, и тогда мне не разрешили редактировать его позже. Поэтому я благодарен (еще раз спасибо @birryree) за это решение, которое помогло в моем конкретном контексте в то время. Спасибо Чарльзу, что заставил меня осознать. – olivecoder 26 August 2015 в 15:42

Плагировать себя из предварительного ответа , чтобы сделать это без риска безопасности, связанного с eval:

expandPath() {
  local path
  local -a pathElements resultPathElements
  IFS=':' read -r -a pathElements <<<"$1"
  : "${pathElements[@]}"
  for path in "${pathElements[@]}"; do
    : "$path"
    case $path in
      "~+"/*)
        path=$PWD/${path#"~+/"}
        ;;
      "~-"/*)
        path=$OLDPWD/${path#"~-/"}
        ;;
      "~"/*)
        path=$HOME/${path#"~/"}
        ;;
      "~"*)
        username=${path%%/*}
        username=${username#"~"}
        IFS=: read _ _ _ _ _ homedir _ < <(getent passwd "$username")
        if [[ $path = */* ]]; then
          path=${homedir}/${path#*/}
        else
          path=$homedir
        fi
        ;;
    esac
    resultPathElements+=( "$path" )
  done
  local result
  printf -v result '%s:' "${resultPathElements[@]}"
  printf '%s\n' "${result%:}"
}

... используется как ...

path=$(expandPath '~/hello')

Альтернативно, более простой подход, который использует eval тщательно:

expandPath() {
  case $1 in
    ~[+-]*)
      local content content_q
      printf -v content_q '%q' "${1:2}"
      eval "content=${1:0:2}${content_q}"
      printf '%s\n' "$content"
      ;;
    ~*)
      local content content_q
      printf -v content_q '%q' "${1:1}"
      eval "content=~${content_q}"
      printf '%s\n' "$content"
      ;;
    *)
      printf '%s\n' "$1"
      ;;
  esac
}
17
ответ дан Charles Duffy 22 August 2018 в 10:51
поделиться
  • 1
    Глядя на ваш код, похоже, вы используете пушку, чтобы убить комара. Есть got , чтобы быть намного проще. – Gino 21 August 2015 в 13:49
  • 2
    @Gino, есть, конечно, более простой способ; вопрос в том, есть ли более простой способ, который также защищен. – Charles Duffy 21 August 2015 в 13:56
  • 3
    @Gino, ... I do предположим, что можно использовать printf %q для удаления всего, кроме тильды, а then использовать eval без риска. – Charles Duffy 21 August 2015 в 13:57
  • 4
    @Gino, ... и так реализовано. – Charles Duffy 21 August 2015 в 14:03
  • 5
    Я только что разместил свое решение по этому вопросу. Он использует регулярное выражение для расширения тильды и должно быть достаточно безопасным. – Gino 21 August 2015 в 14:11

Вы можете найти это проще в python.

(1) Из командной строки unix:

python -c 'import os; import sys; print os.path.expanduser(sys.argv[1])' ~/fred

Результаты в:

/Users/someone/fred

(2) В сценарии bash как одноразовое - сохранить это как test.sh:

#!/usr/bin/env bash

thepath=$(python -c 'import os; import sys; print os.path.expanduser(sys.argv[1])' $1)

echo $thepath

Запуск bash ./test.sh приводит к:

/Users/someone/fred

( 3) В качестве полезности - сохраните это как expanduser где-то на вашем пути с разрешениями на выполнение:

#!/usr/bin/env python

import sys
import os

print os.path.expanduser(sys.argv[1])

Затем это можно использовать в командной строке:

expanduser ~/fred

Или в сценарии:

#!/usr/bin/env bash

thepath=$(expanduser $1)

echo $thepath
0
ответ дан Chris Johnson 22 August 2018 в 10:51
поделиться
  • 1
    Или как насчет передачи только «~» на Python, возврат «/ home / fred» ?? – Tom Russell 8 November 2015 в 09:01
  • 2
    Нужны цитаты moar. echo $thepath глючит; должен быть echo "$thepath" для исправления менее необычных случаев (имена с табуляторами или пробелами, которые преобразуются в одиночные пробелы, имена с расширением глобусов) или printf '%s\n' "$thepath", чтобы исправить необычные (например, файл named -n или файл с символами обратной косой черты в XSI-совместимой системе). Аналогично, thepath=$(expanduser "$1") – Charles Duffy 13 December 2015 в 07:12
  • 3
    ... чтобы понять, что я имел в виду, о литералах обратной косой черты, см. pubs.opengroup.org/onlinepubs/009604599/utilities/echo.html - POSIX позволяет echo вести себя в полностью определенной реализации если любой аргумент содержит обратную косую черту; дополнительные расширения XSI для мандата POSIX по умолчанию (нет необходимости -e или -E) для таких имен. – Charles Duffy 13 December 2015 в 07:13

Вот мое решение:

#!/bin/bash


expandTilde()
{
    local tilde_re='^(~[A-Za-z0-9_.-]*)(.*)'
    local path="$*"
    local pathSuffix=

    if [[ $path =~ $tilde_re ]]
    then
        # only use eval on the ~username portion !
        path=$(eval echo ${BASH_REMATCH[1]})
        pathSuffix=${BASH_REMATCH[2]}
    fi

    echo "${path}${pathSuffix}"
}



result=$(expandTilde "$1")

echo "Result = $result"
1
ответ дан Gino 22 August 2018 в 10:51
поделиться
  • 1
    Кроме того, полагаясь на echo, означает, что expandTilde -n не будет вести себя так, как ожидалось, а поведение с именами файлов, содержащими обратную косую черту, не определено POSIX. См. pubs.opengroup.org/onlinepubs/009604599/utilities/echo.html – Charles Duffy 21 August 2015 в 14:14
  • 2
    Хороший улов. Обычно я использую однопользовательскую машину, поэтому я не думал обрабатывать этот случай. Но я думаю, что эта функция может быть легко улучшена для обработки этого другого случая путем grepping через файл / etc / passwd для другого пользователя. Я оставлю это как упражнение для кого-то другого :). – Gino 21 August 2015 в 14:15
  • 3
    Я уже сделал это упражнение (и обработал случай OLDPWD и другие) в ответ, который вы считаете слишком сложным. :) – Charles Duffy 21 August 2015 в 14:16
  • 4
    на самом деле, я просто нашел довольно простое однострочное решение, которое должно обрабатывать дело otheruser: path = $ (eval echo $ orgPath) – Gino 21 August 2015 в 14:24
  • 5
    FYI: Я только что обновил свое решение, чтобы теперь он мог корректно обрабатывать ~ имя пользователя. И он должен быть достаточно безопасным. Даже если вы вставляете '/ tmp / $ (rm -rf / *)' в качестве аргумента, он должен обрабатывать его изящно. – Gino 21 August 2015 в 17:44

Вот функция POSIX, эквивалентная Bash Håkon Hægland's answer

expand_tilde() {
    tilde_less="${1#\~/}"
    [ "$1" != "$tilde_less" ] && tilde_less="$HOME/$tilde_less"
    printf '%s' "$tilde_less"
}

2017-12-10 edit: добавьте '%s' за @CharlesDuffy в комментариях.

1
ответ дан go2null 22 August 2018 в 10:51
поделиться
  • 1
    printf '%s\n' "$tilde_less", возможно? В противном случае это будет неверно, если расширение файла будет содержать обратные косые черты, %s или другой синтаксис, значимый для printf. Кроме этого, однако, это отличный ответ - правильный (когда расширения bash / ksh не нужно закрывать), очевидно, безопасно (без смешивания с eval) и кратким. – Charles Duffy 8 December 2017 в 22:53

Если переменная var вводится пользователем, eval не следует использовать для расширения тильды с использованием

eval var=$var  # Do not use this!

. Причина в том, что пользователь может случайно (или по назначению ) типа var="$(rm -rf $HOME/)" с возможными катастрофическими последствиями.

Лучшим (и безопасным) способом является использование расширения параметра Bash:

var="${var/#\~/$HOME}"
82
ответ дан Håkon Hægland 22 August 2018 в 10:51
поделиться
  • 1
    Как вы могли бы изменить ~ userName / вместо ~ ~? – aspergillusOryzae 15 December 2014 в 23:43
  • 2
    @aspergillusOryzae Хороший вопрос. Вот обходной путь: stackoverflow.com/a/2069835/2173773 – Håkon Hægland 16 December 2014 в 08:09
  • 3
    В чем цель # в "${var/#\~/$HOME}"? – Jahid 2 June 2015 в 16:46
  • 4
    @Jahid Это объясняется в руководстве . Это заставляет тильду соответствовать только в начале $var. – Håkon Hægland 2 June 2015 в 17:27
  • 5
    Благодарю. (1) Зачем нам нужен \~, т. Е. Экранирование ~? (2) В вашем ответе предполагается, что ~ является первым символом в $var. Как мы можем игнорировать ведущие пробелы в $var? – Tim 5 May 2018 в 01:16
  • 6
    @Tim Спасибо за комментарий. Да, вы правы, нам не нужно избегать тильды, если только это не первый символ строки без кавычек или она следует за [f1] в строке без кавычек. Больше информации в [g0] документах [/g0]. Чтобы удалить начальные пробелы, см. [G1] Как обрезать пробелы в переменной Bash? [/G1] – Håkon Hægland 5 May 2018 в 03:59

Simplest: замените «magic» на «eval echo».

$ eval echo "~"
/whatever/the/f/the/home/directory/is

Проблема: вы столкнетесь с проблемами с другими переменными, потому что eval - это зло. Например:

$ # home is /Users/Hacker$(s)
$ s="echo SCARY COMMAND"
$ eval echo $(eval echo "~")
/Users/HackerSCARY COMMAND

Обратите внимание, что вопрос об инъекции не происходит при первом расширении. Поэтому, если вы просто заменили magic на eval echo, вы должны быть в порядке. Но если вы сделаете echo $(eval echo ~), это будет восприимчиво к инъекции.

Аналогичным образом, если вы eval echo ~ вместо eval echo "~", это будет считаться удвоенным, и, следовательно, инъекция будет возможна сразу .

0
ответ дан Karim Alibhai 22 August 2018 в 10:51
поделиться

Правильно используйте eval правильно: с проверкой.

case $1${1%%/*} in
([!~]*|"$1"?*[!-+_.[:alnum:]]*|"") ! :;;
(*/*)  set "${1%%/*}" "${1#*/}"       ;;
(*)    set "$1" 
esac&& eval "printf '%s\n' $1${2+/\"\$2\"}"
1
ответ дан mikeserv 22 August 2018 в 10:51
поделиться
  • 1
    Вероятно, это безопасно - я не нашел случая, в котором он не работает. Тем не менее, если мы собираемся поговорить с использованием eval «правильно», я бы сказал, что ответ Овервелофила следует лучшей практике: я верю, что printf %q оболочки избегает вещей безопаснее, чем я доверяю рукописному паролю проверки не иметь ошибок. – Charles Duffy 13 December 2015 в 07:25
  • 2
    @Charles Duffy - это глупо. оболочка может не иметь% q - и printf является командой $PATH 'd. – mikeserv 13 December 2015 в 07:29
  • 3
    Разве этот вопрос не отмечен bash? Если это так, printf является встроенным, и %q гарантированно присутствует. – Charles Duffy 13 December 2015 в 07:30
  • 4
    @Charles Duffy - какая версия? – mikeserv 13 December 2015 в 07:31
  • 5
    @Charles Duffy - это ... довольно рано. но я по-прежнему считаю его странным, что вы доверяете% q arg больше, чем вы бы закодировали прямо перед вашими глазами, ive использовал bash достаточно, чтобы знать not , чтобы доверять ему. попробуйте: x=$(printf \\1); [ -n "$x" ] || echo but its not null! – mikeserv 13 December 2015 в 07:35

Расширение (без каламбура) на ответы birryree's и halloleo: общий подход заключается в использовании eval, но он содержит некоторые важные предостережения, а именно пробелы и перенаправление вывода (>) в переменной. Кажется, что для меня работает следующее:

mypath="$1"

if [ -e "`eval echo ${mypath//>}`" ]; then
    echo "FOUND $mypath"
else
    echo "$mypath NOT FOUND"
fi

Попробуйте его с каждым из следующих аргументов:

'~'
'~/existing_file'
'~/existing file with spaces'
'~/nonexistant_file'
'~/nonexistant file with spaces'
'~/string containing > redirection'
'~/string containing > redirection > again and >> again'

Объяснение

  • ${mypath//>} удаляет > символы, которые могут сбивать файл во время eval.
  • eval echo ... - это то, что делает фактическое расширение тильды
  • Двойные кавычки вокруг аргумента -e предназначены для поддержки имен файлов с пробелами.

Возможно, есть более элегантное решение, но это то, что я смог придумать.

7
ответ дан Noach Magedman 22 August 2018 в 10:51
поделиться
  • 1
    Вы можете рассмотреть поведение с именами, содержащими $(rm -rf .). – Charles Duffy 7 September 2015 в 14:46
  • 2
    Разве этот разрыв на путях, которые фактически содержат символы >, хотя? – Radon Rosborough 9 October 2016 в 19:06

Я считаю, что это то, что вы ищете

magic() { # returns unexpanded tilde express on invalid user
    local _safe_path; printf -v _safe_path "%q" "$1"
    eval "ln -sf ${_safe_path#\\} /tmp/realpath.$$"
    readlink /tmp/realpath.$$
    rm -f /tmp/realpath.$$
}

Пример использования:

$ magic ~nobody/would/look/here
/var/empty/would/look/here

$ magic ~invalid/this/will/not/expand
~invalid/this/will/not/expand
2
ответ дан Orwellophile 22 August 2018 в 10:51
поделиться
  • 1
    Я удивлен тем, что printf %q не сбегает из ведущих тильд - почти соблазнительно записать это как ошибку, так как это ситуация, в которой она терпит неудачу в заявленной цели. Однако, тем временем, хороший звонок! – Charles Duffy 21 August 2015 в 14:05
  • 2
    На самом деле - эта ошибка исправлена ​​в какой-то момент между 3.2.57 и 4.3.18, поэтому этот код больше не работает. – Charles Duffy 21 August 2015 в 14:07
  • 3
    Хороший момент, я скорректировал код, чтобы удалить ведущие \ если он существует, поэтому все исправлено и работает :) Я тестировал без цитирования аргументов, поэтому он расширялся до вызова функции. – Orwellophile 25 August 2015 в 12:42
  • 4
    Выглядит хорошо. – Charles Duffy 25 August 2015 в 22:19

Как насчет этого:

path=`realpath "$1"`

Или:

path=`readlink -f "$1"`
6
ответ дан Thor 22 August 2018 в 10:51
поделиться
  • 1
    выглядит красиво, но realpath не существует на моем mac. И вам придется писать path = $ (realpath & quot; $ 1 & quot;) – Hugo 8 July 2011 в 12:09
  • 2
    Привет @Hugo. Вы можете скомпилировать свою собственную команду realpath в C. Например, вы можете сгенерировать исполняемый файл realpath.exe с помощью bash и gcc из этой командной строки: gcc -o realpath.exe -x c - <<< $'#include <stdlib.h> \n int main(int c,char**v){char p[9999]; realpath(v[1],p); puts(p);}'. ура – olibre 24 October 2013 в 10:21
  • 3
    Это "решение" не работает, поскольку @Jay знал бы, пытался ли он это (например, набрав realpath "~" в командной строке). realpath и readlink расширяют ссылки .., но для них ~ - просто простой старый символ; это не имеет никакого отношения к $HOME. – Quuxplusone 23 January 2014 в 20:31
  • 4
    @Quuxplusone не соответствует действительности, по крайней мере для Linux: [f1] - & gt; [F2] – dangonfast 5 October 2018 в 11:00
Другие вопросы по тегам:

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