How to substitute only the highest number in zsh? - zsh

I have a folder with materials for university study, sorted by semesters:
$ ls University
semester1 semester2 semester3 semester4
I'm trying to make one of them the named directory, and I want zsh to allways pointed to directory ending with highest number (so I don't have to update my directory shortcut every semester).
So far I found only the zsh expansion <->:
$ ls semester<->
semester1 semester2 semester3 semester4
but I cannot find a way to extract only the last dirname from that.
Any idea how I should proceed or what I should change?

latestSemester=`ls semester<-> | tail -1`
echo $latestSemester
actually this also works
latestSemester=`ls semester<->([-1])`
EDIT: Fixed the second line, whose first version missed brackets.
From the zsh manual
[beg[,end]]
specifies which of the matched filenames should be included in the returned list. The
syntax is the same as for array subscripts. beg and the optional end may be mathemat-
ical expressions. As in parameter subscripting they may be negative to make them
count from the last match backward. E.g.: ‘*(-OL[1,3])’ gives a list of the names of
the three largest files.

Related

What is the meaning of each parameter for *(*ocNY1) from the shell command `echo`?

I could not find the proper place to look up for the parameter explanation for the below command.
echo *(*ocNY1)
After some tests, I discovered that *(*oc) prints executable files(file with x permission) from the current directory. And NY1 prints the first item of such. But I cannot find the manual for such options. Where can I find the definition/manual for the parameters of such?
Where can I lookup to see the explanation for each parameters for the pattern matching?
Is this glob pattern or regex that echo is using?
Sometimes it is really hard to take the first step if you do not know where you are heading.
*(*ocNY1) is a zsh glob pattern - see man zshexpn.
* is a glob operator that matches any string, including the null string.
The trailing (...) contains glob qualifiers:
* to match executable plain files
oc sort by time of last inode change, youngest first
N sets the nullglob option for the current pattern
Yn expand to at most n filenames

How to ls both alphabetically and by date last modified

So I was trying to do some research on it, but I could not find the answer. So I know that ls -l returns all things in the folder alphabetically, whilst ls -alt returns a list of files by their modification date, though without respect to alphabetical ordering.
I tried doing ls -l -alt, and also ls -alt -l, still no luck. What is the correct way to group them together?
Edit: With example.
Say I have the following list of directories:
aalexand bam carson duong garrett hollande jjackson ksmith mkumba olandt rcs solorzan truong yoo
aalfs battiste chae echo ghamilto holly jkelly kturner mls old.2016 reichman sophia twong zbib
I want to order them by alphabet, so say aalexand comes first. However, if aalfs has been modified last. So in other words has been changed more recently (not really sure how to structure this with proper grammar) it should appear first.
So if this were like a SQL query then we order by date last modified, group by directory name.
I am not sure what you want to do.
But, first of all: ls -l -alt is a double use of the -l parameter (take a look at man ls for more information about the parameters).
ls -l (l stands for list) just lists only one file per line (if you don't need the extra information like permissions, use -1 instead of -l). The -a includes hidden files. -t is for sorting by modified time. You cannot sort by name AND by time, except if two files would have the same name, which is not posible. Could you please explain your wish further?
Maybe you include a short example list of files including their modified time and your desired output, maybe then I can understand.

Learning GREP, command does not work as I've been reading

So I've been given an assignment and the question is:
What command would you enter to see 5-letter words that begin with 'd' (upper or lower-case), followed by a lower-case vowel, and ending in 's'?
grep '^[Dd][aeiouy]..[s]' /usr/share/dict/words
^[Dd] Means that the first letter is D or d. Perfect.
[aeiouy] Means that the next letter will be one of those. Perfect.
Two dots means that the next two characters can be anything that they want. Perfect.
And s because it ends in an s. Perfect.
But when I hit enter, I'm getting things like debasements and debases. Not only are my parameters for grep being ignored, but it is reaching for too many words already, and I can't figure out what I've done wrong.
You need to anchor the end. Like this:
grep '^[Dd][aeiouy]..[s]$' /usr/share/dict/words
Otherwise you're matching all words that start with '[Dd][aeiouy]..s' which is why you get things like "dumpster"
I believe ^ and $ are string terminators, so unless the line contains ONLY the word you're looking for, you won't find it. It only works on the dictionary file but not in general files, if you try. You should use \b on both sides as they're word boundaries.
\b[Dd][aeiouy]..[s]\b
But, grep will not return you only these words. It will return you the whole line that matches the expression, for example:
~$ grep "\b[Dd][aeiouy]..[s]\b" test
aacd danis daniel danis Dunns daniedanilsanielfk
In this case, just use the parameter -o, to print only matching words, one each line.
~$ grep -o "\b[Dd][aeiouy]..[s]\b" test
danis
danis
Dunns

How can I implement the command 'ls' with wildcard, '*'?

EDIT #1 : I'm under the limit that all arguments are enclosed in two quotes, so that shell do not expand any argument with * to the corresponding path.
EDIT #2 : In order to retrieve directories such as */*, ../*, and dirA/*/file.out, How should I use iteration loop or recursive call?
I have just learned about the function fnmatch(). But I don't know start place.
There are many possible cases. I'm confused dealing with these all cases.
For example, Let me assume that executable program is a.out.
$./a.out -l */*
$./a.out -l ../*
$./a.out -l [file_name] [directory_name]
/* Since I also have to implement ls command with no wildcard. */
What should I do? Any advice would be awesome.
Thank you in advance.
Your problem is : shell replaces wildcard caracter * with all of the filenames matching the pattern.
Solution:
If you do not want to use this feature of bash, just put quotation marks around your command line arguments.
Calling your program that way will have the original arguments, containing wildcards.
After this, you can list all the filenames with their paths. For example using some recursive algorithm. Then you can apply some matching to these path string. (when visiting it)
If you want to be a good unix citizen, the rule is Don't do filename globbing unless you are writing a shell.
You want to write an ls-like program? Don't do any wildcard expansion. Don't treat "*" specially. Just treat your argv as a list of filenames. If your program handles these cases:
./a.out file1
./a.out file1 file2 file3
Then it will also handle
./a.out file*
correctly because the shell will do the expansion and your program won't need to know about it. And besides that, it will handle this:
zsh% ./a.out **/file<40-185>~file<90-100>(.mm-30OL[1,2])
which in zsh expanded glob syntax means: expand file40 through file185, except for file90 through file100, include only the ones that have been modified in the last 30 minutes, and use only the largest 2 files in the resulting set.
fnmatch is never going to do anything like that. But these fancy globs can be used with any command that just takes a filename list and doesn't care where it came from.
When you're in a situation where you can't take a list of filenames from the command line, then consider using fnmatch. ls isn't one of those situations.

Unix wildcard selectors? (Asterisks)

In Ryan Bates' Railscast about git, his .gitignore file contains the following line:
tmp/**/*
What is the purpose of using the double asterisks followed by an asterisk as such: **/*?
Would using simply tmp/* instead of tmp/**/* not achieve the exact same result?
Googling the issue, I found an unclear IBM article about it, and I was wondering if someone could clarify the issue.
It says to go into all the subdirectories below tmp, as well as just the content of tmp.
e.g. I have the following:
$ find tmp
tmp
tmp/a
tmp/a/b
tmp/a/b/file1
tmp/b
tmp/b/c
tmp/b/c/file2
matched output:
$ echo tmp/*
tmp/a tmp/b
matched output:
$ echo tmp/**/*
tmp/a tmp/a/b tmp/a/b/file1 tmp/b tmp/b/c tmp/b/c/file2
It is a default feature of zsh, to get it to work in bash 4, you perform:
shopt -s globstar
From http://blog.privateergroup.com/2010/03/gitignore-file-for-android-development/:
(kwoods)
"The double asterisk (**) is not a git thing per say, it’s really a linux / Mac shell thing.
It would match on everything including any sub folders that had been created.
You can see the effect in the shell like so:
# ls ./tmp/* = should show you the contents of ./tmp (files and folders)
# ls ./tmp/** = same as above, but it would also go into each sub-folder and show the contents there as well."
According to the documentation of gitignore, this syntax is supported since git version 1.8.2.
Here is the relevant section:
Two consecutive asterisks (**) in patterns matched against full pathname may have special meaning:
A leading ** followed by a slash means match in all directories. For example, **/foo matches file or directory foo anywhere, the
same as pattern foo. **/foo/bar matches file or directory bar
anywhere that is directly under directory foo.
A trailing /** matches everything inside. For example, abc/** matches all files inside directory abc, relative to the location of
the .gitignore file, with infinite depth.
A slash followed by two consecutive asterisks then a slash matches zero or more directories. For example, a/**/b matches a/b,
a/x/b, a/x/y/b and so on.
Other consecutive asterisks are considered invalid.

Resources