чување 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:
измењених датотека: 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