Om du vill skicka mig elektronisk post så går det bra.
Och om du vill tillbaks till huvudsidan ska det nog också gå för sig.


2007-04-29 10:58 ZSH

Igår försökte jag förbättra mitt liv lite. Det finns många saker man blir ledsen av när man använder datorer, främst för att något som borde vara lätt är svårt eller omöjligt, eller för att något inte fungerar. Idag tänkte jag prata om svårigheten med att döpa om filer.

På typiska moderna datorer använder du ett grafiskt användargränssnitt. Detta anses oftast vara bättre, för det är lättare att lära sig. Detta är antagligen sant, men jag kan redan använda text-baserade gränssnitt som jag oftare finner bättre än jag finner dom sämre, så jag skiter i det. Men just att döpa om filer har alltid varit lite opraktiskt, i det ganska vanliga fallet när man bara vill ändra namnet lite (för man anger först nuvarande namn, och sedan nytt namn, helt separat).

Men det behöver inte vara så. Jag har observerat mig själv döpa om ganska många filer på sista tiden, och hittat flera saker jag kan göra för att göra det lättare. Jag skriver om detta dels för att jag är fantastisk, men också för att ge andra som använder ZSH möjlighet att förbättra även sina liv.

Jag döper ofta om den nyaste filen i en låda. Det vore bra med ett extra smidigt sätt att referera till den. Och det är ju inte så svårt, även om syntaxen givetvis är helt ogenomtränglig. Det behövs bara ett litet alias:

alias -g \\lf="*(.om[1])"

Nu kan jag skriva "mv \lf ostkaka" och döpa om den senaste filen till ostkaka. Men jag vill ju antagligen bara ändra namnet lite. Jag behöver ett sätt att expandera aliaset utan att köra kommandot. Jag tänkte att ZSH gör mycket smarta saker när man trycker på tab, kanske även detta. Men tyvärr var det inte så. Inte heller dom andra widgets (ZSHs praktiskt tydliga term för det) man rimligen kunde haft på tab gör det. (Jag har expand-or-complete-prefix, vilket jag skulle kunna skriva mycket om varför det inte riktigt är vad jag vill ha, men ni får klara er utan.)

Den redan existerande widgeten _expand_alias verkar bara fungera på icke-globala alias (eller rättare sagt, man måste utföra magi med zstyle för att ändra vilka typer den tror på, så det kändes inte värt besväret att lyckas använda den). Den redan existerande widgeten _most_recent_file fungerar visserligen fint, men i verkligheten har jag såklart fler alias jag vill kunna expandera sådär. Så det fick bli en ny funktion:

expand-alias-and-glob() {
	local words word alias
	words=(${(z)LBUFFER})
	word=$words[-1]
	alias=$aliases[$word]
	[[ -z $alias ]] && alias=$galiases[$word]
	if [[ -n $alias ]]; then
		LBUFFER=$LBUFFER[1,$(($#LBUFFER - $#word))]
		LBUFFER+=$alias
	fi
	zle expand-word
}
zle -N expand-alias-and-glob
bindkey '^\' expand-alias-and-glob

Så nu kan jag skriva "mv \lf \lf" och trycka ctrl-\ (jag har \ på en egen tangent, men det fungerar även på min laptop där det blir ctrl-alt-+) och sedan editera destinationsnamnet. Fantastiskt. (Man kan absolut argumentera för att detta borde vara en completion-widget istället, men det är så ogenomträngligt hur man gör sådanna...)

Men så ovanligt är det inte att jag vill döpa om en fil som inte är den senaste. Detta problem visade det sig finnas en inbyggd widget som kunde lösa. Eller ja, filnamnet kan fortfarande vara jobbigt att ange, men jag behöver bara göra det en gång:

bindkey '^x^d' copy-prev-shell-word

Så nu skriver jag alltså "mv 'fil med långt jobbigt namn' " och trycker ^x^d och så står det plötsligt "mv 'fil med långt jobbigt namn' 'fil med långt jobbigt namn'". Nu behöver jag bara ett bättre sätt att navigera runt i detta medans jag editerar det.

Sedan tidigare har jag bundit diverse kombinationer med piltangerna till att hoppa runt på relativt praktiska sätt. Jag har beginning-of-line, end-of-line, vi-backward-word, vi-forward-word, vi-backward-blank-word och vi-forward-blank-word. Detta är nog så praktiskt, när det är det jag vill göra. Men det finns inga shell-(riktning)-word. Jag förstår inte varför. Antagligen för att dom som gör ZSH djupt i sina hjärtan "vet" att filnamn inte har konstiga tecken (som mellanslag...) i sig, och det därför blir samma sak. Men jag har filer med mellanslag i namnen. Faktiskt. Så det fick bli två nya funktioner till:

shell-backward-word() {
	local words word spaces
	words=(${(z)LBUFFER})
	word=$words[-1]
	spaces=-1
	while [[ $LBUFFER[$spaces] == " " ]]; do
		(( spaces-- ))
	done
	(( CURSOR -= $#word - $spaces - 1 ))
}
shell-forward-word() {
	local words word spaces
	words=(${(z)RBUFFER})
	word=$words[1]
	spaces=1
	while [[ $RBUFFER[$spaces] == " " ]]; do
		(( spaces++ ))
	done
	(( CURSOR += $#word + $spaces - 1 ))
}
zle -N shell-backward-word
zle -N shell-forward-word
bindkey '\e[1;5D' shell-backward-word
bindkey '\e[1;5C' shell-forward-word

shell-backward-word flyttar ett tecken för långt om ordet slutar på mellanslag ("foo\ " alltså), men jag ser inget uppenbart sätt att förhindra detta, och väljer att istället leva med det.

Alldeles strax ska jag göra fler magiska funktioner för att editera filnamnen också. En som ersätter mellanslag med _ känns väldigt lockande. Jag tror faktiskt jag ska ta och göra den innan jag publicerar det här.

end-of-shell-word() {
	emulate -L zsh
	local words wordcount lwordcount rwordcount
	words=(${(z)BUFFER})
	wordcount=$#words
	words=(${(z)LBUFFER})
	lwordcount=$#words
	words=(${(z)RBUFFER})
	rwordcount=$#words
	if [[ $wordcount < $(($lwordcount + $rwordcount)) ]]; then
		CURSOR+=$#words[1]
	fi
}
drougge-mangle-filename() {
	emulate -L zsh
	setopt EXTENDED_GLOB
	local words word
	zle end-of-shell-word
	words=(${(z)LBUFFER})
	word=$words[-1]
	LBUFFER=$LBUFFER[1,$(($#LBUFFER - $#word))]
	LBUFFER+=${(qq)${(Q)word//\\ #-\\ #/-}//\ ##/_}
}
zle -N end-of-shell-word
zle -N drougge-mangle-filename

Sådärja. Den tar bort mellanslag runt - också. Syntaxen är en dröm, inte sant?


Föregående    Nästa