0

I wonder if there is a way to remove half the lines of a file using wc and sed.

I can do this:

sed -i '50,$d' myfile.txt

Which removes lines from 50 to the end of file. I can also do this:

wc -l myfile.txt

Which returns the number of lines in the file.

But what I really want to do is something like this:

wc -l myfile.txt | sed -i '{wc -l result}/2,$d' myfile.txt
  1. How can I tell sed to remove the lines starting from the wc -l result divided by 2?
  2. How can I do this recursively?
danielrvt
  • 10,177
  • 20
  • 80
  • 121
  • 1
    use `d` to delete lines, `r` command is used to append text from another file... try `sed -i $(( $(wc -l – Sundeep Dec 10 '19 at 13:27

3 Answers3

2

you can make use of head command:

head -n -"$(($(wc -l<file)/2))" file

awk is also possible, and with exit statement, it could be faster:

awk -v t="$(wc -l <file)" 'NR>=t/2{exit}7' file

You can get the file by awk/head ... > newFile or cmd > tmp && mv tmp file to have "in-place" change.

Kent
  • 189,393
  • 32
  • 233
  • 301
1

I guess you were close. Just use a command substitution with arithmetic expansion to get the value of the starting line:

startline="$(( $(wc -l <myfile.txt) / 2 ))"
sed -i "$startline"',$d' myfile.txt

Or a oneliner:

sed -i "$(( $(wc -l <myfile.txt) / 2 ))"',$d' myfile.txt
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • Thanks, it seems to work but, how can I execute this command for every file in a directory hierarchy recursively? – danielrvt Dec 10 '19 at 13:37
  • I would do `find directory -type f | xargs -d $'\n' -n1 sh -c 'sed -i "$(( $(wc -l <"$1") / 2 )),\$d" "$1"' --` or something along that. I suggest to research that [topic separately](https://stackoverflow.com/questions/10523415/execute-command-on-all-files-in-a-directory). Or something along `find directory -type f -exec sh -c 'sed "$(( $(wc -l <"$1") / 2 )),\$d" "$1"' -- {} \;` – KamilCuk Dec 10 '19 at 13:38
  • This seems to be missing one line unless you add `+ 1` after `/ 2` – Vic Jul 17 '23 at 22:57
1

In some sense, reading the file twice (or, as noted by Kent, once and a half) is unavoidable. Perhaps it will be slightly more efficient if you use just a single process.

awk 'NR==FNR { total=FNR; next }
    FNR>total/2 { exit } 1' myfile.txt myfile.txt

Doing this recursively with Awk is slightly painful. If you have GNU Awk, you can use the -t inline option, but I'm not sure of its semantics when you read the same file twice. Perhaps just fall back to a temporary output file.

find . -type f -exec sh -c "awk 'NR==FNR { total=FNR; next }
    FNR>total/2 { exit } 1' {} {} >tmp &&
  mv tmp {}" _ \;
tripleee
  • 175,061
  • 34
  • 275
  • 318