Can I select only one result from a bash glob?

5,512

Solution 1

The following works in bash 4.2:

list=( /<root_path>/<process_two_path>/logs/* )
echo "${list[-1]}"

If your bash is an older version:

list=( /<root_path>/<process_two_path>/logs/* )
echo "${list[${#list[@]}-1]}"

Solution 2

POSIXly:

 set -- /<root_path>/<process_two_path>/logs/*
 shift "$(($# - 1))"
 printf '%s\n' "$1"

Since you mention zsh:

 print -r /<root_path>/<process_two_path>/logs/*([-1])

Solution 3

Use the bash glob in a bash for loop. Like this:

for filename in <logdir>/*; do :; done; echo "$filename"

This depends on the fact that bash does glob expansion sorted alphabetically. Read the bash manual for more information. Note that this depends on LC_COLLATE. This also depends on the fact that the variable filename still contains the last value after the loop has terminated.

If you want the filename with the oldest timestamp you can do it like this:

for filename in <logdir>/*; do echo "$filename"; break; done

Solution 4

-1 is the default in pipelines, and ls output should already be sorted:

ls | tail -n1

If others were looking for how to insert the first or last result interactively, you can bind menu-complete or menu-complete-backward in .inputrc:

"\e\t": menu-complete
"\e[Z": menu-complete-backward # shift-tab

If show-all-if-ambiguous is enabled, set completion-query-items 0 removes the prompt when there are 101 or more results and set page-completions off disables the pager.

Share:
5,512
AncientSwordRage
Author by

AncientSwordRage

I'm just this guy, y'know? (he/him) Secret ninja-muslim programmer, roleplayer and all-round nuisance. Desh by marriage. I have approximate knowledge of many things Cat thumb-servant. I moderate Sci-fi StackExchange, but I'm active on RPG.se, Puzzling.se and I dabble on the writing and worldbuilding StackExchanges. Sci-Fi I used to be the secretary of my university Sci-fi and fantasy club and I love everything from Horror to 'Saturday morning cartoons'. I help moderate this stack (with a diamond), and I'm active/interested in the marvel, back-to-the-future, star-wars and ghostbusters tags. Happy to meet and greet new users, as well as point people in the right direction for story-id questions. RPG TO BE FILLED StackOverflow TO BE FILLED

Updated on September 18, 2022

Comments

  • AncientSwordRage
    AncientSwordRage almost 2 years

    I'm trying to write a script for work to automate some reporting on an output. The Log files are (currently, it's being 'standardise' in the future) stored in this sort of path structure:

    /<root_path>/<process_one_path>/logs/<time_date_stamp>/<specific_log_file>

    /<root_path>/<process_two_path>/logs/<different_time_date_stamp>/<specific_log_file>

    Every part of the path is known except the time date stamps, which are always the latest in the folder.

    If I try to use a wild card in place of the time date stamp, I get multiple results, e.g.:

    > ls /<root_path>/<process_two_path>/logs/* [tab]
    20130102-175103
    20130118-090859
    20130305-213506
    

    I only want it to return the latest one, is this possible with Bash?

    NB (I don't have zsh, and as lovely as it sounds I doubt we'll ever get it at work)

  • AncientSwordRage
    AncientSwordRage about 11 years
    This definitely works on my home computer but I can't guarantee it'll work at work?
  • phemmer
    phemmer about 11 years
    @Pureferret the part that might not work is the negative array index (-1). This was added in bash 4.2. RHEL5 is probably the oldest supported mainstream enterprise distro available and it uses bash 4.2.20.
  • Stéphane Chazelas
    Stéphane Chazelas about 11 years
    Older versions of bash have: "${list[@]: -1}" like in ksh93 ([-1] comes from zsh).
  • jordanm
    jordanm about 11 years
    @Patrick - RHEL5 uses bash 3.2. RHEL 3 and 4 still supported under extended life.
  • phemmer
    phemmer about 11 years
    @StephaneChazelas That doesn't work when I try it.
  • Stéphane Chazelas
    Stéphane Chazelas about 11 years
    @Patrick, what have you tried. That syntax exists as far back as bash 2.0.3 (15 years ago) at least. Note that you need the space before "-1" to disambiguate from the ${var:-default} syntax,
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' about 11 years
    @Pureferret You can also do arithmetic with the array length, see my edit.
  • Stéphane Chazelas
    Stéphane Chazelas about 11 years
    @Gilles, inside [...] arithmetic expansion is already on so you don't need the $((...)). ${#list} is correct in zsh but not in ksh93 or bash where you need ${#list[@]}
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' about 11 years
    @StephaneChazelas Oops, thanks for the reminder, I always get bitten by that one.
  • rahmu
    rahmu about 11 years
    Note that you shouldn't parse the output of ls. Ever.
  • pyropunk51
    pyropunk51 about 11 years
    Why is that? And how does it differ from using the output from a glob?
  • pyropunk51
    pyropunk51 about 11 years
  • woky
    woky over 7 years
    Is it possible to do it inline? That is without intermediate $list variable.
  • phemmer
    phemmer over 7 years
    @woky I can't think of a way. You could use echo /foo/* | awk '{ print $1 }', but that's pretty dirty, and error prone (spaces break it, etc). You could probably do it in zsh though.
  • Gavin S. Yancey
    Gavin S. Yancey almost 4 years
    Parsing ls is generally a bad idea -- filenames can contain all sorts of weird characters, including anything ls is able to use as a separator.