Вызов функции bash не работает, как следует изменить на недавний каталог?

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

Это оно:

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

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

 $~ cd Desktop/Folder $~ cdrc 

Я получил:

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

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

Существует несколько причин:

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

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

Чтобы выполнить некоторые тесты, вы можете создать неприятный каталог с mkdir "$(echo -ne "foo\nbar")" ; cdrc решения, вероятно, не cdrc если это каталог cdrc должен cd to. Чтобы удалить неприятный каталог, вызовите 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 " ")" ;} 

объяснение

Чтобы объяснить мою функцию, я напишу ее более четко. Обратите внимание, что a \ в самом конце строки говорит 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, секунды с Epoch), одно пробел и имя ( %n ). Из-за опции --printf он не добавляет символ новой строки, а интерпретирует \0 как нулевой символ, который должен быть добавлен в конце каждой строки.
    • {} Является частью синтаксиса find -exec . Во время выполнения find он заменяется именем каталога, поэтому stat знает, какова его цель.
    • + Также является частью синтаксиса find -exec . Это заставляет find передавать несколько имен в один statstat может обрабатывать его). Таким образом, создается меньше процессов stat , это быстрее.

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

 1493488341 directory name 1497365306 troublesome?directory name 

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

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

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

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

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

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

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

Иначе намного проще

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

Условные запросы Linq

Любой способ «разгруппировать» элементы панели задач в Windows?

BasicHttpBinding против WsHttpBinding vs WebHttpBinding

Почему я не могу использовать общую папку VirtualBox?

Как отключить содержимое HTML5 в популярных браузерах, таких как Firefox и Chrome?

Найдите личное поле с Reflection?

Есть ли в Entity Framework 4 Code First поддержка генераторов идентификаторов, таких как NHibernate?

Прочитать файл по строкам в обратном порядке

Windows 7: невозможно просмотреть общие сетевые ресурсы в проводнике, но «сетевой сетевой диск» отлично работает

MAT (Eclipse Memory Analyzer) – как просматривать растровые изображения из дампа памяти

Когда переменная Excel VBA должна быть убита или установлена ​​в Nothing?

Swift 2 (executeFetchRequest): обработка ошибок

ASP.Net MVC – ресурс Не удается найти ошибку

Geospatial $ рядом с текущим значением поля документа

Порядок вызова конструкторов / деструкторов в наследовании

Давайте будем гением компьютера.