Вызов функции bash не работает, как это должно перейти в недавний каталог?

528
snapchatdotcom

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

Это оно:

function cdrc { echo 'cd "$(ls -t | HEAD -1)"'; } 

Когда я хочу перейти на недавний каталог:

$~ cd Desktop/Folder $~ cdrc 

Я получил:

-bash: рабочий стол: команда не найдена

-2

2 ответа на вопрос

1
Kamil Maciorowski

Почему ваша функция не работает

Есть несколько причин:

  • Я думаю echo, это артефакт, используемый для проверки синтаксиса или около того. Нет смысла, если вы хотите, чтобы функция делала то, что вы описали.
  • Прописные HEAD. В Linux это должно быть head. Я не уверен насчет других ОС, где вы можете запустить bashи head. HEADможет или не может работать в некоторых из них, но headдолжно работать везде.
  • Разбор lsне рекомендуется. Об этом есть статья . Главное в вашем случае было бы то, что lsне может надежно печатать имена, включая специальные или непечатные символы.
  • Нет логики проверять только каталоги, вы можете в конечном итоге пытаться cdполучить файл, когда каталога нет.
  • Нет логики обрабатывать ситуацию, когда текущий рабочий каталог пуст.

Все эти проблемы могут быть отлажены, за исключением этого анализа ls. Это недостаток дизайна. Если вы думаете, что lsограничения не будут вас сдерживать, вы можете найти решение из этого другого ответа .

Для выполнения некоторых тестов вы можете создать проблемный каталог с mkdir "$(echo -ne "foo\nbar")"; lsрешения на основе, вероятно, потерпят неудачу, если это каталог cdrcдолжен cd. Чтобы удалить проблемный каталог, вызовите rmdir "$(echo -ne "foo\nbar")".

Мне удалось создать более безопасную функцию.


Решение

function cdrc { cd "$(find -maxdepth 1 -mindepth 1 -type d -exec stat --printf "%Y %n\0" {} + | sort -znr | head -zn 1 | cut -f 2- -d " ")" ;} 

объяснение

Чтобы объяснить мою функцию, я напишу это более четко. Обратите внимание, что \в самом конце строки говорится, bashчто команда продолжается на следующей строке; поэтому мой код ниже рассматривается как однострочный, его можно вставить целиком в интерактивный bash.

function cdrc { \ cd "$( \ find -maxdepth 1 -mindepth 1 -type d -exec \ stat --printf "%Y %n\0" {} + | sort -znr | head -zn 1 | cut -f 2- -d " " \ )" \ ;} 

Процедура выглядит следующим образом:

  • Сначала findвыполняется. Он не спускается в подкаталоги ( -maxdepth 1), он также не находит текущий каталог ( -mindepth 1). Он находит только каталоги ( -type d). Затем statкоманда запускается (спасибо -exec):
    • statпечатает время последней модификации данных ( %Y, mtime, секунды с начала эпохи), один пробел и имя ( %n). Благодаря --printfопции он не добавляет символ новой строки, но интерпретируется \0как нулевой символ, который должен быть добавлен в конце каждой строки.
    • {}является частью find -execсинтаксиса. Во время findвыполнения он заменяется именем каталога, поэтому statзнает, какова его цель.
    • +также является частью find -execсинтаксиса. Это приводит findк передаче нескольких имен к одному statstatможет справиться с этим). Таким образом stat, создается меньше процессов, это быстрее.

На данный момент у нас ноль или более строк. Они выглядят примерно так:

1493488341 directory name 1497365306 troublesome?directory name 

но они заканчиваются нулем, поэтому даже если есть имена с проблемными символами, они будут обработаны правильно. В первом столбце есть mtimes без начальных пробелов (я проверил statповедение с числами различной длины, чтобы быть уверенным), затем первый пробел отделяет mtime от имени каталога.

  • Этот вывод обрабатывается в дальнейшем:
    • sortсортирует строки в соответствии с числовым значением ( -n), использует обратный порядок ( -r) и работает со строками с нулевым символом в конце ( -z). Таким образом, каталог, который нам нужен, теперь находится в первой строке.
    • Затем headоставляет только первую строку ( -n 1); также сказано, что нужно работать со строками с нулевым символом в конце ( -z).
    • cutобрезает строку, обрабатывая пробел как delimiter ( -d " ") и оставляя второе поле и все, что следует ( -f 2-), т.е. все после первого пробела. Работает с пустыми строками ( -z). Окончательный вывод - это желаемое имя каталога.

Обратите внимание, что вывод будет пустым, если в текущем рабочем каталоге нет каталога.

  • $(…)заменяется выводом всего, что внутри. На данный момент у нас есть либо cd "some directory name"или cd "". Первая команда делает то, что вы хотите; последний (когда нет каталога) ничего не делает.

Функция потерпит неудачу, если каталог, в который она должна cdпереместиться / переименована после того, как findнайдет ее. Также statможет выдать ошибку (и), если какой-либо каталог был (пере) перемещен / переименован, когда функция работает

0
tharrrk

Если вы хотите включить все каталоги, т.е. начиная с точки

function cdrc { cd "$(ls -1atp|grep -v '^\.\.\?/$'|grep '/$'|head -1)"; } 

В противном случае гораздо проще

function cdrc { cd "$(ls -1tp|grep '/$'|head -1)"; }