Отримання батьком каталогу в Bash


207

Якщо у мене є шлях до файлу, такий як ...

/home/smith/Desktop/Test
/home/smith/Desktop/Test/

Як змінити рядок, щоб він був батьківським каталогом?

напр

/home/smith/Desktop
/home/smith/Desktop/

4
Ви можете просто використовувати " ..", але, можливо, це не зовсім те, що ви мали на увазі.
Джонатан Леффлер

Відповіді:


331
dir=/home/smith/Desktop/Test
parentdir="$(dirname "$dir")"

Працює, якщо є також косою косою рисою.


14
котирування всередині важливі, або ви втратите всі свої дані
катамфетамін

10
і варіант, який я використовую, щоб отримати батьківського поточного робочого каталогу: parentdir=$(dirname `pwd`)
TheGrimmScientist

3
Зауважте, цей метод небезпечний для "потворних" імен. Таке ім’я, як "-rm -rf", порушить бажану поведінку. Ймовірно "." буде теж. Я не знаю , якщо це найкращий спосіб, але я створив окремий «правильність орієнтований» питання тут: stackoverflow.com/questions/40700119 / ...
VasiliNovikov

4
@Christopher Shroba Так, я пропустив цитати, а потім мій HDD частково витер. Я точно не пам’ятаю, що сталося: імовірно, без котируваного каталогу білого простору змушено було видалити батьківський dir замість цільового - мій сценарій просто видалив папку / home / xxx /.
катамфетамін

3
о добре, значить, нічого про команду не призведе до втрати будь-яких даних, якщо ви вже не видаєте команду delete, правда? Це було лише питанням шляху, який ви намагалися видалити, розбившись на пробіл і видаливши набагато більше, ніж очікувалося?
Крістофер Шроба

18

... але те, що " тут бачимо ", порушено. Ось виправлення:

> pwd
/home/me
> x='Om Namah Shivaya'
> mkdir "$x" && cd "$x"
/home/me/Om Namah Shivaya
> parentdir="$(dirname "$(pwd)")"
> echo $parentdir
/home/me

14

Просто використовуйте echo $(cd ../ && pwd)під час роботи в каталозі, чий батьківський dir ви хочете дізнатися. Ця ланцюг також має додаткову перевагу від того, що вона не має задніх косих.


Вам тут не потрібно echo.
gniourf_gniourf

14

Очевидно, що батьківський каталог задається простим додаванням імені файлу dot-dot:

/home/smith/Desktop/Test/..     # unresolved path

Але ви хочете вирішити шлях (абсолютний шлях без будь-яких компонентів шляху-крапки):

/home/smith/Desktop             # resolved path

Проблема з головними відповідями, які використовуються dirname, полягає в тому, що вони не працюють, коли ви вводите шлях з крапками:

$ dir=~/Library/../Desktop/../..
$ parentdir="$(dirname "$dir")"
$ echo $parentdir
/Users/username/Library/../Desktop/..   # not fully resolved

Це більш потужно :

dir=/home/smith/Desktop/Test
parentdir=`eval "cd $dir;pwd;cd - > /dev/null"`

Ви можете годувати його /home/smith/Desktop/Test/.., але також і більш складні шляхи, такі як:

$ dir=~/Library/../Desktop/../..
$ parentdir=`eval "cd $dir;pwd;cd - > /dev/null"`
$ echo $parentdir
/Users                                  # the fully resolved path!

EDIT: Якщо це не працює, переконайтеся, що ваш файл cdне змінено, як це іноді є звичайною практикою,

$ which cd
cd is a function  #cd was modified to print extra info, so use "builtin cd" to override
cd ()
{
    builtin cd "$@";
    ls -FGAhp
}
...

Використовувати evalНЕ чудово, якщо є якийсь шанс проаналізувати введення користувача, який може перенести вас кудись, де ви не очікуєте, або не почнете поганих дій щодо вашої системи. Чи можете ви використовувати pushd $dir 2>&1 >/dev/null && pwd && popd 2>&1 >/dev/nullзамість eval?
dragon788

2
Вам зовсім не потрібно eval: просто робіть parentdir=$(cd -- "$dir" && pwd). Оскільки cdзапуск виконується в передпласті, вам не потрібно cdповертатися туди, де ми були. Тут --є на випадок, якщо розширення $dirпочинається з дефісу. Це &&лише для того, щоб не parentdirмати порожнього вмісту, коли це cdне вдалося.
gniourf_gniourf

8

Мотивація для іншої відповіді

Мені подобається дуже короткий, чіткий, гарантований код. Бонусний бал, якщо він не запускає зовнішню програму, оскільки в той день, коли вам потрібно буде обробити величезну кількість записів, це буде помітно швидше.

Принцип

Не впевнені, які гарантії ви маєте та хочете, тому пропонуйте все одно.

Якщо у вас є гарантії, ви можете зробити це за допомогою дуже короткого коду. Ідея полягає у використанні функції заміщення тексту bash для скорочення останньої косої риски та будь-якого іншого.

Відповідь від простих до складніших випадків оригінального питання.

Якщо шлях гарантовано закінчиться без косої риски (увімкнення та виїзд)

P=/home/smith/Desktop/Test ; echo "${P%/*}"
/home/smith/Desktop

Якщо шлях гарантовано закінчиться рівно однією косою рисою

P=/home/smith/Desktop/Test/ ; echo "${P%/*/}/"
/home/smith/Desktop/

Якщо шлях введення може закінчуватися нулем або однією косою рисою (не більше), і ви хочете, щоб вихідний шлях закінчувався без косої риски

for P in \
    /home/smith/Desktop/Test \
    /home/smith/Desktop/Test/
do
    P_ENDNOSLASH="${P%/}" ; echo "${P_ENDNOSLASH%/*}"
done

/home/smith/Desktop
/home/smith/Desktop

Якщо вхідний шлях може мати багато сторонніх косої риси, і ви хочете, щоб вихідний шлях закінчувався без косої риски

for P in \
    /home/smith/Desktop/Test \
    /home/smith/Desktop/Test/ \
    /home/smith///Desktop////Test// 
do
    P_NODUPSLASH="${P//\/*(\/)/\/}"
    P_ENDNOSLASH="${P_NODUPSLASH%%/}"
    echo "${P_ENDNOSLASH%/*}";   
done

/home/smith/Desktop
/home/smith/Desktop
/home/smith/Desktop

1
Фантастична відповідь. Оскільки здається, що він шукав спосіб зробити це всередині скрипту (знайдіть поточний каталог сценарію та його батьків і зробіть щось, що стосується місця, де живе сценарій), це чудова відповідь, і ви можете мати ще більший контроль над тим, на вході є косою косою рисою чи ні, використовуючи розширення параметра, проти ${BASH_SOURCE[0]}якого було б повний шлях до сценарію, якщо він не був отриманий через sourceабо ..
dragon788

7

Якщо /home/smith/Desktop/Test/../ви хочете:

dirname 'path/to/child/dir'

як видно тут .


1
вибачте, я маю на увазі, як у баш-скрипті, у мене є змінна з файлом шлях
YTKColumba

Запуск цього коду дає мені помилку bash: dirname 'Test': syntax error: invalid arithmetic operator (error token is "'Test'"). Пов'язаний код також неправильний.
Майкл Гофман

спробуйте просто запустити dirname Testз каталогу Test.
Джон Егеланд

1

Залежно від того, чи потрібні вам абсолютні шляхи, ви можете зробити додатковий крок:

child='/home/smith/Desktop/Test/'
parent=$(dirname "$child")
abs_parent=$(realpath "$parent")

0

скористайтеся цим: export MYVAR="$(dirname "$(dirname "$(dirname "$(dirname $PWD)")")")"якщо ви хочете 4-й батьківський каталог

export MYVAR="$(dirname "$(dirname "$(dirname $PWD)")")" якщо ви хочете 3-й батьківський каталог

export MYVAR="$(dirname "$(dirname $PWD)")" якщо ви хочете 2-й батьківський каталог


Дякую !, саме те, що я шукав.
Абарка

0

потворний, але ефективний

function Parentdir()

{

local lookFor_ parent_ switch_ i_

lookFor_="$1"

#if it is not a file, we need the grand parent
[ -f "$lookFor_" ] || switch_="/.."

#length of search string
i_="${#lookFor_}"

#remove string one by one until it make sens for the system
while [ "$i_" -ge 0 ] && [ ! -d "${lookFor_:0:$i_}" ];
do
    let i_--
done

#get real path
parent_="$(realpath "${lookFor_:0:$i_}$switch_")" 

#done
echo "
lookFor_: $1
{lookFor_:0:$i_}: ${lookFor_:0:$i_}
realpath {lookFor_:0:$i_}: $(realpath ${lookFor_:0:$i_})
parent_: $parent_ 
"

}

    lookFor_: /home/Om Namah Shivaya
{lookFor_:0:6}: /home/
realpath {lookFor_:0:6}: /home
parent_: /home 


lookFor_: /var/log
{lookFor_:0:8}: /var/log
realpath {lookFor_:0:8}: /UNIONFS/var/log
parent_: /UNIONFS/var 


lookFor_: /var/log/
{lookFor_:0:9}: /var/log/
realpath {lookFor_:0:9}: /UNIONFS/var/log
parent_: /UNIONFS/var 


lookFor_: /tmp//res.log/..
{lookFor_:0:6}: /tmp//
realpath {lookFor_:0:6}: /tmp
parent_: / 


lookFor_: /media/sdc8/../sdc8/Debian_Master//a
{lookFor_:0:35}: /media/sdc8/../sdc8/Debian_Master//
realpath {lookFor_:0:35}: /media/sdc8/Debian_Master
parent_: /media/sdc8 


lookFor_: /media/sdc8//Debian_Master/../Debian_Master/a
{lookFor_:0:44}: /media/sdc8//Debian_Master/../Debian_Master/
realpath {lookFor_:0:44}: /media/sdc8/Debian_Master
parent_: /media/sdc8 


lookFor_: /media/sdc8/Debian_Master/../Debian_Master/For_Debian
{lookFor_:0:53}: /media/sdc8/Debian_Master/../Debian_Master/For_Debian
realpath {lookFor_:0:53}: /media/sdc8/Debian_Master/For_Debian
parent_: /media/sdc8/Debian_Master 


lookFor_: /tmp/../res.log
{lookFor_:0:8}: /tmp/../
realpath {lookFor_:0:8}: /
parent_: /

0

Почав із ідеї / коментаря Чарльз Даффі - 17 грудня 14 о 14:32 на тему Отримати назву поточного каталогу (без повного шляху) у сценарії Bash

#!/bin/bash
#INFO : /programming/1371261/get-current-directory-name-without-full-path-in-a-bash-script
# comment : by Charles Duffy - Dec 17 '14 at 5:32
# at the beginning :



declare -a dirName[]

function getDirNames(){
dirNr="$(  IFS=/ read -r -a dirs <<<"${dirTree}"; printf '%s\n' "$((${#dirs[@]} - 1))"  )"

for(( cnt=0 ; cnt < ${dirNr} ; cnt++))
  do
      dirName[$cnt]="$( IFS=/ read -r -a dirs <<<"$PWD"; printf '%s\n' "${dirs[${#dirs[@]} - $(( $cnt+1))]}"  )"
      #information – feedback
      echo "$cnt :  ${dirName[$cnt]}"
  done
}

dirTree=$PWD;
getDirNames;

0

якщо з якоїсь - небудь причини ви зацікавлені в навігації вгору певну кількість каталогів , які ви могли б також зробити: nth_path=$(cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && cd ../../../ && pwd). Це дозволить створити 3 каталогів батьків


чому $ {BASH_SOURCE [0]} замість простішого $ BASH_SOURCE
Nam G VU
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.