0

CONTEXT

I am in a directory with 3 folders. After executing ls -1 they are ordered like so:

folder1
folder2
folder3

I want to quickly cd into folder2.

I was able to write a function to quickly cd into folder1

this is my function.

f1 () {
       cd $(ls -d */|head -n 1)
}

QUESTION

I need a function called f2. That when executed, cds into folder2.


(the folders are sorted by ls -1. I am using zsh)

5

Shell globs are ordered in lexicographic order by default, and Zsh allows indexing the results of a glob directly, so you could do something like this:

cd2() {
    cd -- */([2]);       # indexing starts at one
}

In Bash, you could do the same with an array:

cd2() {
    local dirs=(*/)
    cd -- "${dirs[1]}"   # indexing starts at zero
}

Both essentially assume that there are at least two matches to the glob. See Kusalananda's answer for ways to deal with that assumption.

Also, if you have ls aliased so that it's given some flags, the sort order might be different from that of the shells'. (I have ls aliased to ls -vF --color=auto on Linux, the -v changes even the sort order of a and _.) Zsh would of course give you multiple other options for sorting, too.


Note that while you could use something like ls | head -2 | tail -1 to get the second filename in the list of ls output, that doesn't work for filenames with newlines and involves not one but three external processes to do something the shell is capable of doing itself, so it's not a very good solution. ls */ is even sillier, since here it's the shell that expands the glob, and ls just prints out the names it received. See https://mywiki.wooledge.org/ParsingLs for more about how relying on the output of ls can be problematic.

3

Using zsh, the following would cd into the second directory in the current directory:

cd ./*(/[2])

The / inside the parenthesis is a modifier of the * glob that makes it only match directory names (see the zshexpn manual), and [2] extract the second directory that this glob matches.

Wrapping this in a function:

cd2nd () { cd ./*(/[2]); }

Note that this would take you to your home directory if there is no second directory in the current directory. We could protect against that by first testing whether the second directory exists:

cd2nd () { [ -d ./*(/[2]) ] && cd ./*(/[2]); }

But now we expand the pattern twice.

In /bin/sh (or zsh), with only a single glob matching:

cd2nd () { set -- ./*/; [ -d "$2" ] && cd "$2"; }

This uses the positional parameters to hold all subdirectory names (*/ only matches directories), tests the second one for existence, and changes to that directory if it existed.

1

Function that takes argument with number of directory it should change to

function ff()
{
    cd "$(ls -d */ | head -n $1 | tail -n 1)";
}

Usage:

user@localhost:~ $ ff 2
user@localhost:~/Desktop $

Explanation:

  • ls -d */ lists all directories in current directory
  • head -n $1 lists only $1 first directories, and $1 is our function argument, so if you call cc 2 then $1 would take value of 2 (first two lines are now being processed).
  • tail -n 1 picks only last line
  • All of this is being send to cd command, so directory is changed to proper one (quotes are there so directory names with spaces will be parsed as one argument)

Warning!

This version is assuming everything is ok with input. You should validate input (both from user and from filesystem directory list) to avoid unexpected errors in the future.

  • It would fail on any second directory name that contains whitespaces, and might do unexpected thing if there are directory names with shell globbing characters in their names (a directory called *, for example). – Kusalananda Feb 11 at 12:02
  • @Kusalananda: Thanks for pointing it out. Fixed with proper quotes and tested with directory names containing both spaces and special characters like '*'. – DevilaN Feb 11 at 18:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.