xargs generiert keinen korrekten Befehl

7

Ich möchte eine Reihe von Apps auf einem Android-Gerät löschen, die alle mit demselben Paket beginnen. Ich erhalte diese mit folgendem Befehl:

$ adb shell ls /data/data | grep -i com.company
com.company.android.app.adwidget
com.company.android.app.attendancereports
com.company.android.app.atteventmanagement
com.company.android.app.buttonwidget
com.company.android.app.clockwidget

Jetzt möchte ich adb uninstallfür jeden dieser Paketnamen Folgendes ausführen xargs: Ich dachte daran, Folgendes zu verwenden :

$ adb shell ls /data/data | grep -i com.company | xargs -n1 echo adb uninstall
adb uninstall com.company.android.app.adwidget
adb uninstall com.company.android.app.attendancereports
adb uninstall com.company.android.app.atteventmanagement
adb uninstall com.company.android.app.buttonwidget
adb uninstall com.company.android.app.clockwidget

Es sieht so aus, als würde es funktionieren, also entferne ich echo:

$ adb shell ls /data/data | grep -i com.company | xargs -n1 adb uninstall
Failure
Failure
Failure
Failure
Failure

Wenn Sie jedoch jeden Befehl unabhängig ausführen, erhalten Sie Success:

$ adb uninstall com.company.android.app.adwidget
Success

Was mache ich falsch?

m0skit0
quelle
1
Keine Lösung für Ihr Problem, aber Sie brauchen das nicht grep, verwenden Sie einfach einen Globus: adb ls /data/data/*com.company* | xargs -n1 adb uninstallsollten Sie das Gleiche tun. Funktioniert es, wenn Sie es tun for i in adb ls /data/data/*com.company*; do adb uninstall "$i"; done?
Terdon
Ah, sorry, das hätte sein sollen adb shell ls, nicht adb ls, macht das einen Unterschied machen?
Terdon
1
@terdon, wenn Sie diesen Glob nicht zitieren, wird er durch die lokale Shell erweitert, nicht durch die Shell, die unter Android ausgeführt wird. Die Ausgabe wird auch Dinge wie /data/data/somethingstatt sein something.
Stéphane Chazelas
@ StéphaneChazelas Richtig, es nicht zu zitieren gibt mir immer noch no matches found. Das Zitieren verhält sich genauso wie in meiner Frage ( Failurefür alle Hinrichtungen)
m0skit0

Antworten:

8

Obwohl das Problem letztendlich durch CR-Zeichen in der adb shellAusgabe verursacht wurde (eingefügt durch die tty-Zeilendisziplin des auf dem Ziel-Android-System erstellten pty (siehe hier für weitere Details)), eine andere mögliche Erklärung (und ich werde es dort belassen Für zukünftige Leser, da dies ein häufiges Problem ist, xargskönnte dies das gewesen sein in:

adb shell ls /data/data | grep -i com.company | xargs -n1 adb uninstall

Abhängig von der xargsImplementierung ist adbstdin entweder /dev/nulloder die Pipe von grep. In jedem Fall wird es nicht das tty sein, und das kann der Grund sein, warum es adbfehlschlägt, wenn es erwartet, mit dem Benutzer interagieren zu können.

Mit GNU xargsund einer Shell mit Unterstützung für die Prozessersetzung (wie zsh) können Sie Folgendes ändern:

xargs -n1 -ra <(adb shell ls /data/data | grep -i com.company) adb uninstall

In diesem Fall wird xargsdie Liste aus der als Argument angegebenen Datei gelesen, mit der -aSie stdin in Ruhe lassen können.

Oder da Sie erwähnen zsh, könnten Sie verwenden:

autoload zargs # best in ~/.zshrc
zargs -L1 $(adb shell ls /data/data | grep -i com.company) -- adb uninstall

(Verwenden von -Lanstelle von -nas zargs's -nbegrenzt die Gesamtzahl der Argumente auf adb(einschließlich des uninstalleinen), was bedeutet, dass wir benötigen würden -n 2).

Oder verwenden Sie einfach eine Schleife, die in diesem Fall noch kürzer und besser lesbar wäre:

for x ($(adb shell ls /data/data | grep -i com.company)) adb uninstall $x
Stéphane Chazelas
quelle
Ihre xargsund for-Befehle geben den gleichen Fehler wie meine xargs-Version aus: Failurefür alle Ausführungen. zargsBefehl gibt mir zargs: argument list too long(danke zargsübrigens)
m0skit0
1
@ m0skit0, wickeln Sie die forSchleife ein (set -x; foo loop here)und sehen Sie, wo sich das adbvon Hand unterscheidet. Möglicherweise enthält die Ausgabe einige nachgestellte Wagenrücklaufzeichen (aka $'\C-M').
Stéphane Chazelas
Coole Funktion. Ich sehe keinen Unterschied, hier ist die erste Befehlsausgabe : +zsh:48> x=com.company.android.app.attendancereports +zsh:48> adb uninstall com.company.android.app.attendancereports Failure. Ich sehe keine seltsamen Charaktere.
m0skit0
@ m0skit0 und adb uninstall com.company.android.app.attendancereportsfunktioniert? Möglicherweise müssen Sie eine Verzögerung zwischen den adbBefehlen einfügen ?
Stéphane Chazelas
1
@ m0skit0. Ja, ich kann mit dieser Version reproduzieren. Mit einer neueren Version erhalten Sie die $'\C-M'oben gezeigte. Geheimnis gelüftet.
Stéphane Chazelas
4

adb shell ls /data/data | grep -i com.companyAls ich die Ausgabe in eine Datei umleitete und sie mit einem Hexeditor untersuchte, stellte ich fest, dass sie an einen Wagenrücklauf im Windows-Stil \r\n (0x0D 0x0A) angehängt sind . Also von der Loswerden \rmit tr -d '\r'dem Problem gelöst.

Ganzer Befehl mit for(aus der Antwort von Stéphane Chazelas ):

for x in $(adb shell ls /data/data | grep -i com.company | tr -d '\r'); do adb uninstall $x;  done

Oder ähnlich mit xargs:

adb shell ls /data/data | grep -i com.company | tr -d '\r' | xargs -r -n1 adb uninstall

Eine andere Option (wie freundlicherweise von Stéphane Chazelas in den Kommentaren unten erläutert) ist das \rvollständige Deaktivieren mit stty -opost, obwohl dies höchstwahrscheinlich die Installation busybox(oder eine Alternative wie diese toybox) auf dem Android-Gerät erfordert.

$ adb shell echo test | sed -n l               
test\r$
$ adb shell 'busybox stty -opost; echo test' | sed -n l
test$
m0skit0
quelle
2
adb shell 'stty -opost; ls /data/data'könnte auch funktionieren (wenn diese CReingeführt werden, weil adbsie mit einem Pseudo-Terminal auf der Android-Seite interagieren).
Stéphane Chazelas
@ StéphaneChazelas Ich hatte keine Zeit zum Testen, da jetzt alle Pakete deinstalliert wurden. Ich muss einige erneut installieren, um zu testen.
m0skit0
Das Übertragen von Binärdaten über die ADB-Shell (dh die schnelle Dateiübertragung mit tar) legt nahe, dass dies der Fall ist. stty rawbeinhaltet stty -opostwas beinhaltet stty -onlcr. Wenn Sie sich dazu eine Manpage ansehen, adbkönnen Sie adb shell -tdiese Pty-Zuordnung möglicherweise auch deaktivieren!
Stéphane Chazelas
Für Ihren ersten Vorschlag : /system/bin/sh: stty: not found\r$. Darüber -tfunktioniert es auch nicht : error: target doesn't support PTY args -Tt. Bitte beachten Sie, dass dies ein ziemlich altes Android-Gerät (4.1) ist.
m0skit0
Aber mit busyboxmacht der Trick in der Tat: adb shell 'busybox stty -opost; echo test' | sed -n lAusgaben test$während adb shell echo test | sed -n lAusgaben test\r$. Vielen Dank!
m0skit0