ste

Једноставни уређивач табела
git clone https://git.sr.ht/~strahinja/ste
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување c4cec2c847a8b13053332b10aca53a93407fe62b
родитељ 7a3e05c27c833e02abf4f84c9061cebf81ee237f
Аутор: Страхиња Радић <contact@strahinja.org>
Датум:   Sun, 22 Oct 2023 20:07:46 +0200

Make vipe optional by transforming tsv* commands to use stdin/stdout

Signed-off-by: Страхиња Радић <contact@strahinja.org>

Diffstat:
MINSTALL | 4++--
MTODO | 2+-
Mconfig.redo | 2+-
Mste.in | 49++++++++++++++++++++++++++++++++++++++++++++-----
Atest/students.tsv | 3+++
Mtsvedit | 60++++++++++++++++++++++++++++++++++++++----------------------
Mtsvedit.1.in | 14++++----------
Mtsvins | 43+++++++++++++++----------------------------
Mtsvins.1.in | 11++---------
Atsvselect | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atsvselect.1.in | 50++++++++++++++++++++++++++++++++++++++++++++++++++
измењених датотека: 11, додавања: 246(+), брисања: 78(-)

diff --git a/INSTALL b/INSTALL @@ -4,14 +4,14 @@ Prerequisites * Standard utilities: awk, cat, cp, cut, diff, getopts, grep, head, mv, printf, rm, sed, tail, touch, trap, wc * table[1] -* vipe from moreutils[2] -* Any text editor set via EDITOR environment variable Optional -------- * gzip (autodetected, for manpage compression) +* vipe from moreutils[2] + any text editor set via EDITOR environment variable; + if not present, reads stdin * rlwrap[3] (autodetected, fallback if absent) for command line editing [1]: https://strahinja.srht.site/table/ diff --git a/TODO b/TODO @@ -1,7 +1,7 @@ TODO ==== -< > Transform tsv* to work as stdin/stdout filters +<x> Transform tsv* to work as stdin/stdout filters < > Command to insert/delete a column? diff --git a/config.redo b/config.redo @@ -7,6 +7,6 @@ MANPREFIX=$PREFIX/share/man # OpenBSD #MANPREFIX=$PREFIX/man -PROGS="ste transpose tsvdel tsvedit tsvfind tsvins tsvmove" +PROGS="ste transpose tsvdel tsvedit tsvfind tsvins tsvmove tsvselect" MANSUFFIX=${MANSUFFIX-$(command -v gzip >/dev/null && printf ".gz")} MANPAGES=$(for prog in $PROGS; do printf "%s.1%s\n" "$prog" "$MANSUFFIX"; done) diff --git a/ste.in b/ste.in @@ -25,11 +25,16 @@ editprog=tsvedit findprog=tsvfind insprog=tsvins moveprog=tsvmove +selprog=tsvselect tableprog=tsvtable # Variables which normally should not be modified by user DATE="%DATE%" VERSION="%VERSION%" +editfilter='' +if command -v vipe >/dev/null 2>&1; then + editfilter="vipe --suffix tsv" +fi error() { @@ -234,11 +239,13 @@ while [ "$running" -eq 1 ]; do if [ -z "$nextcmd" ]; then if command -v rlwrap >/dev/null; then + #shellcheck disable=SC2016,SC2140 cmd=$(rlwrap -H "$historyfile" -s 1000 -D 2 -I -o \ -S "${file##*/}[${_cols}x${_rows}+${firstrow}"\ "@${currow}/${maxrows}]> " sh -c 'IFS= read -r CMD && printf "%s\n" "$CMD"') + #shellcheck disable=SC2181 if [ "$?" -ne 0 ]; then - cmd=exit + cmd="exit" fi else printf "%s[%dx%d+%d@%d/%d]> " \ @@ -261,6 +268,7 @@ while [ "$running" -eq 1 ]; do else args='' fi + #shellcheck disable=SC2086 case "$cmd" in /) set -- $args searchcol='' @@ -341,7 +349,14 @@ while [ "$running" -eq 1 ]; do fi redraw=1 ;; - edit|e) ${editprog} "$file" "$((currow + 1))" + edit|e) if [ "$editfilter" ]; then + ${selprog} "$file" "$((currow + 1))" | ${editfilter} | + ${editprog} "$file" "$((currow + 1))" + else + error "warning: vipe not detected; input replaces row" + ${selprog} "$file" "$((currow + 1))" + cat | ${editprog} "$file" "$((currow + 1))" + fi redraw=1 ;; exit|q) running=0 @@ -366,12 +381,36 @@ while [ "$running" -eq 1 ]; do ;; insert|i) case "$args" in - -) ${insprog} "$file" "$((currow))" + -) if [ "$editfilter" ]; then + ${selprog} "$file" "$((maxrows + 1))" | + ${editfilter} | + ${insprog} "$file" "$((currow))" + else + error "warning: vipe not detected" + ${selprog} "$file" "$((maxrows + 1))" + cat | ${insprog} "$file" "$((currow))" + fi redraw=1;; - +) ${insprog} "$file" "$((currow + 1))" + +) if [ "$editfilter" ]; then + ${selprog} "$file" "$((maxrows + 1))" | + ${editfilter} | + ${insprog} "$file" "$((currow + 1))" + else + error "warning: vipe not detected" + ${selprog} "$file" "$((maxrows + 1))" + cat | ${insprog} "$file" "$((currow + 1))" + fi currow=$((currow + 1)) redraw=1;; - "") ${insprog} "$file" + "") if [ "$editfilter" ]; then + ${selprog} "$file" "$((maxrows + 1))" | + ${editfilter} | + ${insprog} "$file" + else + error "warning: vipe not detected" + ${selprog} "$file" "$((maxrows + 1))" + cat | ${insprog} "$file" + fi currow=$((maxrows)) redraw=1;; *) error "Invalid argument: '%s'" "$args";; diff --git a/test/students.tsv b/test/students.tsv @@ -0,0 +1,3 @@ +ID Name Age +1 John 25 +2 Peter 33 diff --git a/tsvedit b/tsvedit @@ -5,8 +5,6 @@ # any later version. Copyright (C) 2023 Страхиња Радић. # See the file LICENSE for exact copyright and license details. -# Needs vipe from moreutils and $EDITOR https://joeyh.name/code/moreutils/ - tab=$(printf "\t") nl=' ' @@ -37,37 +35,55 @@ case $# in esac tmpf=${inputfile}~ - [ -w "${inputfile}" ] || { error "\`${inputfile}' not writeable"; exit 1; } -command -v vipe >/dev/null 2>&1 || { error "vipe not found"; exit 1; } - -cp "${inputfile}" "$tmpf" -# shellcheck disable=SC2064 -trap "rm -f \"$tmpf\"" HUP PIPE INT QUIT TERM EXIT +numlines=$(awk 'END {print NR}' "${inputfile}") -if [ -z "${lineno}" ]; then - lineno=$(cut -d"$tab" -f"${colno}" "${inputfile}" \ - | grep -ni "${searchtext}" \ - | sed 's/^\([0-9]\+\).*/\1/;1q') - [ -z "$lineno" ] \ - && { error "not found \`${searchtext}' in column ${colno}" +# shellcheck disable=SC2086 +if [ ! $lineno ]; then + lineno=$(cut -d"$tab" -f"${colno}" -- "${inputfile}" | + grep -n "${searchtext}" | sed 's/^\([0-9]\+\).*/\1/;1q') + # shellcheck disable=SC2086 + [ ! $lineno ] && + { error "not found \`${searchtext}' in column ${colno}" exit 1; } fi +savelineno="${lineno}" +# shellcheck disable=SC2030,SC2086 +(while [ $lineno ]; do + case $(printf "%s\n" "${lineno}" | cut -c 1) in + [0-9]) ;; + *) error "argument not a number: \`${savelineno}'" + exit 1 + ;; + esac + # shellcheck disable=SC2030 + lineno=$(printf "%s\n" "${lineno}" | cut -c 2-) +done) || exit 1 + +# shellcheck disable=SC2031 +if [ "${lineno}" -gt "${numlines}" ]; then + error "line number ${lineno} greater than the number of lines, \ +${numlines}" + exit 1 +fi + +# shellcheck disable=SC2031 if [ "${lineno}" -lt 1 ]; then error "line number must be positive: \`${lineno}'" exit 1 fi -{ head -n$((lineno-1)) "${inputfile}" - # shellcheck disable=SC1003 - { sed 1q "${inputfile}" | sed -e 's/^/# /' -e 's/'"$tab"'/'"$tab"'# /g' - head -n"$lineno" "${inputfile}" | tail -n1; } | - transpose - | sed 's/'"$tab"'/\'"$nl"'/g' | - vipe --suffix tsv | +touch -- "$tmpf" +# shellcheck disable=SC2064 +trap "rm -f \"$tmpf\"" HUP PIPE INT QUIT TERM EXIT + +# shellcheck disable=SC2031 +{ head -n$((lineno-1)) -- "${inputfile}" + # shellcheck disable=SC1003 sed '/^#/d;s/\([^'"$tab"']*\)$/\1'"$tab"'/g' | tr -d '\n' | - sed 's/'"$tab"'$/\'"$nl"'/g' - tail +$((lineno+1)) "${inputfile}"; } > "$tmpf" + sed 's/'"$tab"'$/\'"$nl"'/g' + tail +$((lineno+1)) -- "${inputfile}"; } > "$tmpf" if diff "$tmpf" "${inputfile}" >/dev/null 2>&1; then rm "$tmpf" diff --git a/tsvedit.1.in b/tsvedit.1.in @@ -9,26 +9,20 @@ .Ar tsvfile.tsv .Op Ar rowno | Ar colno Ar text .Sh DESCRIPTION -. +Read lines from stdin and interpret them as columns of a line to replace the +line from a TSV file; .Bl -tag -width ".Nm Ar rowno" -offset indent . .It Nm Ar tsvfile.tsv Ar rowno -select the line (row) to be edited by its row number (1\-based) +select the line (row) to be replaced by its row number (1\-based) . .It Nm Ar tsvfile.tsv Ar colno Ar text -select the line (row) to be edited by searching the column with index +select the line (row) to be replaced by searching the column with index .Ar colno (1\-based) for .Ar text . . .El -.Sh IMPLEMENTATION NOTES -Needs -.Xr vipe 1 -from moreutils -.Lk https://joeyh.name/code/moreutils/ -and any -.Ev $EDITOR . .Sh AUTHORS .An "Strahinya Radich" Aq contact@strahinja.org , 2023 diff --git a/tsvins b/tsvins @@ -5,8 +5,6 @@ # any later version. Copyright (C) 2023 Страхиња Радић. # See the file LICENSE for exact copyright and license details. -# Needs vipe from moreutils and $EDITOR https://joeyh.name/code/moreutils/ - tab=$(printf "\t") nl=' ' @@ -25,7 +23,7 @@ error() case $# in 1) inputfile=$1 - lineno=0 + lineno='' ;; 2) inputfile=$1 lineno=$2 @@ -38,37 +36,26 @@ esac [ ! -f "${inputfile}" ] && touch -- "${inputfile}" tmpf=${inputfile}~ -numlines=$(wc -l -- "${inputfile}" | awk '{print $1}') - +numlines=$(awk 'END {print NR}' "${inputfile}") +# shellcheck disable=SC2086 +[ ! $lineno ] && lineno=$((numlines+1)) command -v vipe >/dev/null 2>&1 || { error "vipe not found"; exit 1; } -cp -- "${inputfile}" "$tmpf" +touch -- "$tmpf" # shellcheck disable=SC2064 trap "rm -f -- \"$tmpf\"" HUP PIPE INT QUIT TERM EXIT -if [ $# -eq 2 ]; then - head -n"${lineno}" -- "${inputfile}" > "$tmpf" -fi -if [ "${numlines}" -gt 0 ]; then +{ if [ "${numlines}" -gt 0 ]; then + if [ "${lineno}" -gt 0 ]; then + head -n"${lineno}" -- "${inputfile}" + fi + fi # shellcheck disable=SC1003 - { sed 1q "${inputfile}" | sed -e 's/^/# /' -e 's/'"$tab"'/'"$tab"'# /g' - # shellcheck disable=SC1003 - sed 1q "${inputfile}" | - sed -e 's/[^'"$tab"']*\('"$tab"'\?\)/\1/g'; } | - transpose - | sed -e 's/'"$tab"'/\'"$nl"'/g' | - vipe --suffix tsv | - sed '/^#/d;s/\([^'"$tab"']*\)$/\1'"$tab"'/g' | - tr -d '\n' | - sed 's/'"$tab"'$/\'"$nl"'/g' >> "$tmpf" -else - # shellcheck disable=SC1003 - vipe --suffix tsv </dev/null | - sed 's/\([^'"$tab"']*\)$/\1'"$tab"'/g' | tr -d '\n' | - sed 's/'"$tab"'$/\'"$nl"'/g' >> "$tmpf" -fi -if [ $# -eq 2 ]; then - tail +$((lineno + 1)) -- "${inputfile}" >> "$tmpf" -fi + sed '/^#/d;s/\([^'"$tab"']*\)$/\1'"$tab"'/g' | tr -d '\n' | + sed 's/'"$tab"'$/\'"$nl"'/g' + if [ "${numlines}" -gt 0 ]; then + tail +$((lineno + 1)) -- "${inputfile}" + fi; } > "$tmpf" if diff -- "$tmpf" "${inputfile}" >/dev/null 2>&1; then rm -- "$tmpf" diff --git a/tsvins.1.in b/tsvins.1.in @@ -9,8 +9,8 @@ .Ar tsvfile.tsv .Op Ar lineno .Sh DESCRIPTION -. -Insert a line to a TSV file after the line +Read lines from stdin and interpret them as columns of a line to insert to a TSV +file after the line .Ar lineno (1-based). When 0 is passed as @@ -19,13 +19,6 @@ insert a line as first (header) line. If .Ar lineno is omitted, insert a line at the end of file. -.Sh IMPLEMENTATION NOTES -Needs -.Xr vipe 1 -from moreutils -.Lk https://joeyh.name/code/moreutils/ -and any -.Ev $EDITOR . .Sh AUTHORS .An "Strahinya Radich" Aq contact@strahinja.org , 2023 diff --git a/tsvselect b/tsvselect @@ -0,0 +1,86 @@ +#!/bin/sh +# vim: set ft=bash: +# tsvselect - Select a line from a TSV file +# This program is licensed under the terms of GNU GPL v3 or (at your option) +# any later version. Copyright (C) 2023 Страхиња Радић. +# See the file LICENSE for exact copyright and license details. + +tab=$(printf "\t") +nl=' +' + +usage() +{ + cat <<EOT +Usage: ${0##*/} tsvfile.tsv [rowno | colno text] +EOT +} + +error() +{ + printf "%s: %s\n" "${0##*/}" "$@" >&2 +} + +case $# in + 2) inputfile=$1 + lineno=$2 + ;; + 3) inputfile=$1 + colno=$2 + searchtext=$3 + ;; + *) usage + exit 1 + ;; +esac + +# Exit silently without failing if the file doesn't exist. This enables tsvins +# to work on new files, not existing up to that point +[ ! -f "${inputfile}" ] && exit + +[ -w "${inputfile}" ] || { error "\`${inputfile}' not writeable"; exit 1; } +numlines=$(awk 'END {print NR}' "${inputfile}") +command -v vipe >/dev/null 2>&1 || { error "vipe not found"; exit 1; } + +# shellcheck disable=SC2086 +if [ ! $lineno ]; then + lineno=$(cut -d"$tab" -f"${colno}" -- "${inputfile}" | + grep -n "${searchtext}" | sed 's/^\([0-9]\+\).*/\1/;1q') + # shellcheck disable=SC2086 + [ ! $lineno ] && + { error "not found \`${searchtext}' in column ${colno}" + exit 1; } +fi + +savelineno="${lineno}" +# shellcheck disable=SC2086 +(while [ $lineno ]; do + case $(printf "%s\n" "${lineno}" | cut -c 1) in + [0-9]) ;; + *) error "argument not a number: \`${savelineno}'" + exit 1 + ;; + esac + # shellcheck disable=SC2030 + lineno=$(printf "%s\n" "${lineno}" | cut -c 2-) +done) || exit 1 + +# shellcheck disable=SC2031 +if [ "${lineno}" -lt 1 ]; then + error "line number must be positive: \`${lineno}'" + exit 1 +fi + +if [ "${numlines}" -gt 0 ]; then + # shellcheck disable=SC1003 + { sed 1q "${inputfile}" | + sed -e 's/^/# /' -e 's/'"$tab"'/'"$tab"'# /g' + # shellcheck disable=SC2031 + if [ "${lineno}" -le "${numlines}" ]; then + head -n"${lineno}" -- "${inputfile}" | tail -n1 + else + sed 1q "${inputfile}" | + sed -e 's/[^'"$tab"']*\('"$tab"'\?\)/\1/g' + fi; } | + transpose - | sed -e 's/'"$tab"'/\'"$nl"'/g' +fi diff --git a/tsvselect.1.in b/tsvselect.1.in @@ -0,0 +1,50 @@ +.Dd %DATE% +.Dt TSVSELECT 1 +.Os +.Sh NAME +.Nm tsvselect +.Nd Select a line from a TSV file +.Sh SYNOPSIS +.Nm +.Ar tsvfile.tsv +.Op Ar rowno | Ar colno Ar text +.Sh DESCRIPTION +Output a line (row) from a TSV file, with each column being output on a separate +line, and preceded by a line containing a hashmark ( +.Li # +) followed by space and +.Dq column title ; +.Bl -tag -width ".Nm Ar rowno" -offset indent +. +.It Nm Ar tsvfile.tsv Ar rowno +select the line (row) to be output by its row number (1\-based) +. +.It Nm Ar tsvfile.tsv Ar colno Ar text +select the line (row) to be output by searching the column with index +.Ar colno +(1\-based) for +.Ar text . +. +.El +. +For example, for the TSV file +.Pa students.tsv : +.Bd -literal +ID Name Age +1 John 25 +2 Peter 33 +.Ed +.Ql tsvselect students.tsv 3 +will output: +.Bd -literal +# ID +2 +# Name +Peter +# Age +33 +.Ed +. +.Sh AUTHORS +.An "Strahinya Radich" Aq contact@strahinja.org , +2023