display a line every two line (osX) zsh - macos

I'd to display every two line, a line from a file. I've seen the sed -n 'f~d' awk and perl method. But the sed one doesn't work on osX (As I understood) and the two others are are interpreted languages which i can't use.
Can you help me ?
Here's an exemple :
output before :
-rw-r--r-- 1 mfassi-f 2013 22 Jul 17 12:36 test.sh
-rw-r--r-- 1 mfassi-f 2013 29 Jul 17 12:30 test1.sh
-rw-r--r-- 1 mfassi-f 2013 22 Jul 17 12:36 test2.sh
-rw-r--r-- 1 mfassi-f 2013 29 Jul 17 12:30 test3.sh
-rw-r--r-- 1 mfassi-f 2013 22 Jul 17 12:36 test4.sh
-rw-r--r-- 1 mfassi-f 2013 29 Jul 17 12:30 test5.sh
-rw-r--r-- 1 mfassi-f 2013 22 Jul 17 12:36 test6.sh
output after :
-rw-r--r-- 1 mfassi-f 2013 29 Jul 17 12:30 test1.sh
-rw-r--r-- 1 mfassi-f 2013 29 Jul 17 12:30 test3.sh
-rw-r--r-- 1 mfassi-f 2013 29 Jul 17 12:30 test3.sh
-rw-r--r-- 1 mfassi-f 2013 29 Jul 17 12:30 test5.sh

Here are two answers. One for a file, and one for command-line input.
['cause the question's changed ever so slightly, but these two seemed too similar to put as independent answers].
You can use zsh, ls, cut and paste to do this in a for loop. It's not the cleanest solution, but it does work (surprisingly).
for file in `ls -1 | paste - - | cut -f 1`
do
ls -l -d $file
done
We take the output of ls -1, then extract every second filename. (The way ls chooses to sort the files will have an impact here). Then, we do ls -l -d on each of these files. -d is necessary to stop ls from showing us the contents of $file, if $file is a directory. (Not sure if this is OS X specific, or if that's default POSIX ls behaviour).
Second answer: display every second line from a file.
If you're after a mostly zsh solution, you could do something like the following:
$ jot 8 0 7 >> sample.txt # Generate some numbers.
$ count=0 # Storage variable
$ for i in `cat sample.txt`
do
if [ $(( $count % 2 )) -eq 0 ] ; then
echo $i
fi
count=`expr $count + 1`
done
This displays every second line.
Notes:
- This leaves a variable count in your session afterwards (it's not clean).
- This fails badly if sample.txt does not contain a single word per line.
- I'm almost sure that the modulus comparison I do isn't the most efficient: I grabbed it from here.
- I say it's mostly zsh because it does rely on cat, but I'm not sure how to avoid that.

The OS X version of sed is frustrating. Using sed -n '0~2p' <filename> doesn't work because, in the BSD sed, -n does something different:
-n
By default, each line of input is echoed to the standard output after all of the commands have been applied to it. The -n option suppresses this behavior.
I'd highly recommend installing GNU sed, which can be done using Homebrew:
brew install gnu-sed
And then you can use:
gsed -n '0~2p' filename # Display the 2nd, 4th etc
gsed -n '1~2p' filename # Display the 1st, 3rd etc.

Related

How to rename a lot of files

I have directory of files called:
foo--ext1
foo--ext2
foo--ext3
...
I would like to rename them to:
foo-bar-ext1
foo-bar-ext2
foo-bar-ext3
....
How can I do this renaming in bash?
I have attempted to understand mywiki.wooledge.org/BashFAQ/030 but I can't work what should go in place of ${f%.foo}.bar"; in the first example.
So I have started with:
for f in foo--*; do echo mv -- "$f"
but what do I put next?
There are several ways to approach this.
I recommend bookmarking this page and referencing it often.
I would use this:
$: for f in foo--*; do mv "$f" "${f//foo--/foo-bar-}"; done
This uses string substitution in the current filename to construct the new name, replacing foo-- with foo-bar-.
Note the // in the replacement. This will replace every occurrence of foo-- with foo-bar- in each filename.
$: ls -l foo-*
-rw-r--r-- 1 paul 1049089 0 Jul 26 14:32 foo-bar-ext1
-rw-r--r-- 1 paul 1049089 0 Jul 26 14:32 foo-bar-ext2
-rw-r--r-- 1 paul 1049089 0 Jul 26 14:32 foo-bar-ext3
-rw-r--r-- 1 paul 1049089 0 Jul 26 14:33 foo-bar-foo-bar-etx4
Remove one of the leading slashes to make it only handle the first occurrence -
$: for f in foo--*; do mv "$f" "${f/foo--/foo-bar-}"; done
$: ls -l foo-*
-rw-r--r-- 1 paul 1049089 0 Jul 26 14:34 foo-bar-ext1
-rw-r--r-- 1 paul 1049089 0 Jul 26 14:34 foo-bar-ext2
-rw-r--r-- 1 paul 1049089 0 Jul 26 14:34 foo-bar-ext3
-rw-r--r-- 1 paul 1049089 0 Jul 26 14:34 foo-bar-foo--ext4
Another simple method avoiding the loop is to use rename (from the util-linux package). There all that is needed is:
rename "foo-" "foo-bar" foo--*
You can check what will be done before actually doing the rename with the -n (no-act) and -v (verbose) options. For your example files, that would be:
$ rename -nv "foo-" "foo-bar" foo--*
`foo--ext1' -> `foo-bar-ext1'
`foo--ext2' -> `foo-bar-ext2'
`foo--ext3' -> `foo-bar-ext3'
There are two versions of rename that you will find provided in Linux distributions, the rename above from the util-linux package and perl-rename, which some Linux distros use instead which will also install as rename. Both are capable of handling the rename, but the options will be different. Check which you have with rename --version before use.

Terminal Piping and Writing to File

I am trying to copy the first two items in my 'Downloads' directory using only the terminal.
I open up zsh, cd into my 'Downloads' directory and start typing.
The below reflects what is shown in the terminal:
% ls -lt | head -3
file1.csv
file2.csv (exactly the files I want)
% ls -lt | head -3 > ToBeCopied.txt
% vim ToBeCopied.txt
total 24625744
-rw-r--r-- 1 Aaron staff 0 22 Apr 15:28 ToBeCopied.txt
-rw-r--r--# 1 Aaron staff 42042 22 Apr 15:16 file1.csv
What happened to file2.csv?

Redirection to a variable has weird behaviour

I have this small script:
#!/bin/bash
for file in "$(ls | grep -v $0)";do
cat $file > "${file}-test"
done
On this directory:
total 40
-rwxr-xr-x 1 root root 783 Dec 11 09:19 appendToLog.sh
-rwxr-xr-x 1 root root 3995 Dec 11 13:22 con2dd.py
-rwxr-xr-x 1 root root 362 Dec 11 13:26 dd.py
-rw-r--r-- 1 root root 566 Dec 11 13:26 dd.pyc
-rw-r--r-- 1 root root 18558 Dec 25 11:24 moshe.log
-rw------- 1 root root 0 Dec 11 09:20 nohup.out
-rwxr-xr-x 1 root root 88 Dec 25 11:28 task.sh
-rwxr-xr-x 1 root root 560 Dec 11 10:33 test.py
Nevermind that I can achieve that with cp, I want to understand why this exactly is producing this file:
-rw-r--r-- 1 root root 24912 Dec 25 11:28 appendToLog.sh?con2dd.py?dd.py?dd.pyc?moshe.log?nohup.out?task.sh?test.py-test
And nothing else.
The problem is parsing output of ls is just wrong (see Why you shouldn't parse the output of ls(1), filenames in unix can have almost any special characters including whitespace, newlines, commas, pipe symbols. Its because you've quoted the output of ls in one construct, you have a single list of all the files concatenated as one string in the value of "${file}-test" which is quite not what you wanted to do.
Also notice how ls sometimes garbles your filename data (in our case, it turned the \n character in between the words into a ? question mark (could indicate a character that cannot be displayed).
Just use the glob expansion in bash to list the files and do actions on them.
for f in *; do
[[ -e $f ]] || continue
...
done
That said, You could probably have some non-printable characters on end of lines (eg. CRLF imported rom Windows)
Run cat -A scriptname it'll show you all characters in your script. Then, you can convert to unix-like format running dos2unix scriptname.

Why the shell doesn't write the good hour and give me the year?

I'm a little bit confused, i changed my time on one file with the shell command :
touch -t = touch -t 201606012135 trial01
But after the ls -lt, I got this :
-rw-r--r-- 1 CharleyD staff 87 1 jun 2016 trial01
drwxr-xr-x 15 CharleyD staff 510 3 apr 12:57 Hybrid_proj
Why the shell doesn't write the hours like the "Hybrid_proj" directory for the "trial01" ? The trial01 file have the hour : 21:35, so itsn't empty.
Indeed, I search to get this in output :
-rw-r--r-- 1 CharleyD staff 87 1 jun 21:35 trial01
drwxr-xr-x 15 CharleyD staff 510 3 apr 12:57 Hybrid_proj
How I can do this ?
Thx a lot buddies to enlighten my way ! ;)
If a file is not from the current year, ls defaults to showing the year instead of the time. The time is still correctly set, just formatted differently.
To always show the full time, with GNU ls, you can use ls --time-style=long-iso -l:
$ ls --time-style=full-iso -l
total 0
-rw-r--r-- 1 user user 0 2017-04-04 13:20 newfile
-rw-r--r-- 1 user user 0 2016-04-03 12:34 oldfile
With BusyBox ls, you can use -e:
$ busybox ls -e
total 0
-rw-r--r-- 1 user user 0 Tue Apr 4 13:20:42 2017 newfile
-rw-r--r-- 1 user user 0 Sun Apr 3 12:34:00 2016 oldfile
With macOS ls, you can use -lT:
$ ls -lT
total 0
-rw-r--r-- 1 user group 0 Apr 4 13:19:35 2017 myfile
-rw-r--r-- 1 user group 0 Apr 3 12:34:00 2016 oldfile
In each case, you get a long timestamp with the same format for older and newer files.
Use the -T option if your ls supports it.

Find newest file in folder via the terminal (seconds apart)

I am trying to extract the newest file in folder.
I tried this:
ls -1t | head -1
But the problem is, that this command doesn't seem to differentiate in which second the file was created - if multiple files were created in the the same, this command just give the first file in this minute.
Is there a way to make this command more precise?
You should add T argument to the options to do that:
ls -1tT | head -1
See the output when l argument is added:
ls -tTl
-rw-r--r-- 1 user wheel 0 Apr 23 17:54:27 2016 1
-rw-r--r-- 1 user wheel 0 Apr 23 17:54:19 2016 3
-rw-r--r-- 1 user wheel 0 Apr 23 17:54:12 2016 2

Resources