Detecteren als pad er een specifieke map-invoer bevat

Met /bin/bash, hoe zou ik detecteren of een gebruiker een specifieke map heeft in hun $ PATH-variabele?

Bijvoorbeeld

if [ -p "$HOME/bin" ]; then
  echo "Your path is missing ~/bin, you might want to add it."
else
  echo "Your path is correctly set"
fi

1, Autoriteit 100%

Gebruik grepis overkill, en kan problemen veroorzaken als u op zoek bent naar iets dat toevallig re-metacharacters bevat. Dit probleem kan perfect worden opgelost met BASH’s ingebouwde [[opdracht:

if [[ ":$PATH:" == *":$HOME/bin:"* ]]; then
  echo "Your path is correctly set"
else
  echo "Your path is missing ~/bin, you might want to add it."
fi

Houd er rekening mee dat het toevoegen van dubbele punten vóór de uitbreiding van $ pad en het pad naar zoeken naar oplost het substringmatch-kwestie; Dubbel-citaten Het pad vermijdt problemen met metacacters.


2, Autoriteit 12%

Hier is hoe het te doen zonder grep:

if [[ $PATH == ?(*:)$HOME/bin?(:*) ]]

De sleutel hier is om de Colons en Wildcards optioneel te maken met behulp van de ?()Construct. Er mogen geen enkel probleem zijn met metacharacters in dit formulier, maar als u citaten wilt opnemen, is dit waar ze gaan:

if [[ "$PATH" == ?(*:)"$HOME/bin"?(:*) ]]

Dit is een andere manier om het te doen met behulp van de Match-operator (=~), zodat de syntaxis meer lijkt op grep‘s:

if [[ "$PATH" =~ (^|:)"${HOME}/bin"(:|$) ]]

3, Autoriteit 10%

Er is absoluut niet nodig om externe hulpprogramma’s zoals grephiervoor te gebruiken. Hier is wat ik heb gebruikt, wat draagbaar moet zijn naar zelfs oudere versies van de Bourne Shell.

case :$PATH: # notice colons around the value
  in *:$HOME/bin:*) ;; # do nothing, it's there
     *) echo "$HOME/bin not in $PATH" >&2;;
esac

4

Hier is een pure-bash-implementatie die geen valse positieven opneemt vanwege gedeeltelijke matching.

if [[ $PATH =~ ^/usr/sbin:|:/usr/sbin:|:/usr/sbin$ ]] ; then
  do stuff
fi

Wat gebeurt hier? The = ~ Operator maakt gebruik van regex patroonondersteuning die aanwezig is in bash beginnend met versie 3.0. Drie patronen worden gecontroleerd, gescheiden door regex of exploitant |.

Alle drie de subpatronen zijn relatief vergelijkbaar, maar hun verschillen zijn belangrijk voor het vermijden van gedeeltelijke wedstrijden.

In regex komt ^overeen met het begin van een regel en $komt overeen met het einde. Zoals geschreven, wordt het eerste patroon alleen geëvalueerd als waar als het pad waarnaar het zoekt de eerste waarde binnen $PATH is. Het derde patroon evalueert alleen waar als het pad waarnaar het zoekt de laatste waarde binnen $PATH is. Het tweede patroon evalueert waar als het het pad vindt dat het zoekt tussen andere waarden, omdat het zoekt naar het scheidingsteken dat de variabele $PATH gebruikt, :, aan weerszijden van het pad dat wordt weergegeven. gezocht.


Antwoord 5

Ik heb de volgende shell-functie geschreven om te rapporteren of een map wordt vermeld in het huidige PATH. Deze functie is POSIX-compatibel en werkt in compatibele shells zoals Dash en Bash (zonder afhankelijk te zijn van Bash-specifieke functies).

Het bevat functionaliteit om een ​​relatief pad om te zetten in een absoluut pad. Het gebruikt hiervoor de readlinkof realpathutilities, maar deze tools zijn niet nodig als de meegeleverde directory geen ..of andere links als componenten heeft van zijn pad. Afgezien hiervan vereist de functie geen programma’s buiten de shell.

# Check that the specified directory exists – and is in the PATH.
is_dir_in_path()
{
  if  [ -z "${1:- }" ]; then
    printf "The path to a directory must be provided as an argument.\n" >&2
    return 1
  fi
  # Check that the specified path is a directory that exists.
  if ! [ -d "$1" ]; then
    printf "Error: ‘%s’ is not a directory.\n" "$1" >&2
    return 1
  fi
  # Use absolute path for the directory if a relative path was specified.
  if command -v readlink >/dev/null ; then
    dir="$(readlink -f "$1")"
  elif command -v realpath >/dev/null ; then
    dir="$(realpath "$1")"
  else
    case "$1" in
      /*)
        # The path of the provided directory is already absolute.
        dir="$1"
      ;;
      *)
        # Prepend the path of the current directory.
        dir="$PWD/$1"
      ;;
    esac
    printf "Warning: neither ‘readlink’ nor ‘realpath’ are available.\n"
    printf "Ensure that the specified directory does not contain ‘..’ in its path.\n"
  fi
  # Check that dir is in the user’s PATH.
  case ":$PATH:" in
    *:"$dir":*)
      printf "‘%s’ is in the PATH.\n" "$dir"
      return 0
      ;;
    *)
      printf "‘%s’ is not in the PATH.\n" "$dir"
      return 1
      ;;
  esac
}

Het deel dat :$PATH:gebruikt, zorgt ervoor dat het patroon ook overeenkomt als het gewenste pad het eerste of laatste item is in het PATH. Deze slimme truc is gebaseerd op dit antwoord van Glenn Jackman op Unix & Linux.


Antwoord 6

$PATHis een lijst met strings gescheiden door :die een lijst met mappen beschrijven. Een directory is een lijst met strings gescheiden door /. Twee verschillende strings kunnen naar dezelfde map verwijzen (zoals $HOMEen ~, of /usr/local/binen /usr/local/bin/). We moeten dus de regels vastleggen van wat we willen vergelijken/controleren. Ik stel voor om de hele strings te vergelijken/controleren, en niet de fysieke mappen, maar dubbele en achterliggende /te verwijderen.

Verwijder eerst dubbele en achterliggende /van $PATH:

echo $PATH | tr -s / | sed 's/\/:/:/g;s/:/\n/g'

Veronderstel nu dat $dde map bevat die u wilt controleren. Pipetteer vervolgens het vorige commando om $din $PATHaan te vinken.

echo $PATH | tr -s / | sed 's/\/:/:/g;s/:/\n/g' | grep -q "^$d$" || echo "ontbreekt $d"

Antwoord 7

Dit is een brute force-benadering, maar het werkt in alle gevallen, behalve wanneer een padinvoer een dubbele punt bevat. En er worden geen andere programma’s gebruikt dan de shell.

previous_IFS=$IFS
dir_in_path='no'
export IFS=":"
for p in $PATH
do
  [ "$p" = "/path/to/check" ] && dir_in_path='yes'
done
[ "$dir_in_path" = "no" ] && export PATH="$PATH:/path/to/check"
export IFS=$previous_IFS

Other episodes