Como executar a mesma operação que pressionar o botão de ejeção no nautilus, executando um comando?

2

Eu gostaria de ejetar um dispositivo executando um comando (para que ele possa ser atribuído a um atalho-chave, por exemplo).

Eu usei udisksctl e sei como ejetar uma unidade, mas isso não é muito útil para atribuir a uma vinculação de chave, porque.

  • Erros são reportados no terminal (o qual eu gostaria de ser notificado) .
  • Detalhes sobre o sucesso também são impressos no terminal (que eu quero ignorar)

Existe uma maneira de executar um comando que ejeta um dispositivo que notifica erros, como se eu tivesse pressionado ejetar no nautilus?

    
por ideasman42 21.07.2017 / 06:39

3 respostas

2

Aqui está um script python que reunirá todas as partições USB montadas e executará udisksctl unmount -b <DEV> em cada uma delas. As regras padrão para tornar isso um atalho de trabalho se aplicam: certifique-se de que o script seja executável e forneça o caminho completo para o script como comando.

Conforme solicitado, apenas os erros serão exibidos na caixa de diálogo da GUI, outra saída não será mostrada.

#!/usr/bin/env python
import os
import subprocess
import sys

def run_cmd(cmdlist):
    """ utility: reusable function for running external commands """
    try:
        stdout = subprocess.check_output(cmdlist,stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError as cpe:
        # GtkDialog error should be ignored. 
        if not "GtkDialog mapped without a transient parent" in cpe.output:
            return error_dialog(cpe.output," ".join(cmdlist))

def error_dialog(err_msg,called_cmd):
    """ Displays graphical error dialog and exits the script"""
    subprocess.call(['zenity','--error','--text',err_msg,'--title',called_cmd])
    sys.exit(1)

def find_usb_partitions():
    """ Constructs a list of USB partitions """
    return tuple( os.path.realpath(os.path.join("/dev/disk/by-path",p))
                  for p in os.listdir("/dev/disk/by-path")
                  if 'usb' in p and 'part' in p
    )  

def find_mounted_devs():
    devs=[]
    with open('/proc/mounts') as mounts:
         for line in mounts:
             dev = line.split()[0]
             if dev.startswith('/dev/'):
                 devs.append(dev)     
    return devs

def main():
   parts=find_usb_partitions()
   devs=find_mounted_devs()
   mounted_parts=tuple(i for i in parts if i in devs)
   for part in mounted_parts:
       run_cmd(['udisksctl', 'unmount', '-b',part])
       run_cmd(['udisksctl', 'power-off', '-b',part])

if __name__ == '__main__': main()

Pensamentos adicionais

Enquanto o script funciona, acho a ideia de desmontar do atalho um pouco redundante. Tal tarefa requer pelo menos algum envolvimento de um usuário. Se o objetivo é ser capaz de desmontar sem terminal, já existem maneiras de fazer isso. Por exemplo, se você é um usuário do Unity, você já tem o ícone do dispositivo no lançador, do qual você pode clicar com o botão direito e ejetar o dispositivo. Eu escrevi Udisks-Indicator , que você pode usar para desmontar partições de um indicador gráfico (e que usa algumas das mesmas idéias que usei nesta resposta). Em ambos os casos, já existem alternativas, mas eu pessoalmente aconselharia o uso de atalhos para desmontar as coisas. Mas, novamente, esta é apenas minha opinião pessoal.

    
por Sergiy Kolodyazhnyy 21.07.2017 / 09:56
1

Esta é uma modificada resposta excelente do @sergiy-kolodyazhnyy com as seguintes mudanças:

  • Não procure por part no nome (algumas de minhas unidades não usam partições).
  • Use by-id em vez de by-path para que um p.startswith("usb-") menos ambíguo possa ser usado.
  • Desligue em um loop separado (vários dispositivos podem estar conectados à mesma energia).
  • Verifique se o dispositivo existe antes de desligar (novamente, fonte de energia ligada).
  • Executar comandos ( run_cmd ) retorna um booleano baseado no sucesso.
  • Notifique o resultado final (número de dispositivos montados / desmontados).
  • Use o Python3 (saída de subprocesso em bytes).

Atualização: tornou esta ferramenta mais genérica:
udisksctl_usb_all [--mount, --unmount, --unmount-power-off]
Por isso pode ser usada para todas as 3 operações.

#!/usr/bin/env python3
import os
import subprocess
import sys


def run_cmd(cmdlist):
    ''' utility: reusable function for running external commands '''
    try:
        stdout = subprocess.check_output(cmdlist, stderr=subprocess.STDOUT)
        return True
    except subprocess.CalledProcessError as cpe:
        print(cpe.output)
        # GtkDialog error should be ignored.
        if not b'GtkDialog mapped without a transient parent' in cpe.output:
            return error_dialog(cpe.output, ' '.join(cmdlist))
        return False


def error_dialog(err_msg, called_cmd):
    ''' Displays graphical error dialog and exits the script'''
    print(called_cmd)
    subprocess.call(['zenity', '--error', '--text', err_msg, '--title', called_cmd])
    sys.exit(1)


def find_usb_partitions():
    ''' Constructs a list of USB partitions '''
    return tuple(
        os.path.realpath(os.path.join('/dev/disk/by-id', p))
        for p in os.listdir('/dev/disk/by-id')
        if p.startswith('usb-')
    )


def find_mounted_devs():
    devs = []
    with open('/proc/mounts') as mounts:
        for line in mounts:
            if line.startswith('/dev/'):
                devs.append(line.split(' ', 1)[0])
    return devs


def unmount_all(power_off=False):
    unmount_len = 0
    parts = find_usb_partitions()
    devs = find_mounted_devs()
    mounted_parts = tuple(i for i in parts if i in devs)
    for part in mounted_parts:
        if run_cmd(['udisksctl', 'unmount', '-b', part]):
            unmount_len += 1

    if power_off:
        # Some drives may be linked regarding power, check each exists first.
        for part in mounted_parts:
            if os.path.exists(part):
                run_cmd(['udisksctl', 'power-off', '-b', part])

    if unmount_len:
        run_cmd(['notify-send', 'Unmounted {} file-systems!'.format(unmount_len)])
    else:
        run_cmd(['notify-send', 'Nothing to unmount!'])


def mount_all():
    mount_len = 0
    parts = find_usb_partitions()
    devs = find_mounted_devs()

    # only include non-numbered devices if numbered ones don't exist
    # don't try to mout /dev/sdc if /dev/sdc1 exists.
    parts_numbered = set()
    for p in parts:
        p_strip = p.rstrip('0123456789')
        if p != p_strip:
            parts_numbered.add(p_strip)
    parts = tuple(p for p in parts if p not in parts_numbered)
    del parts_numbered

    unmounted_parts = tuple(i for i in parts if i not in devs)
    for part in unmounted_parts:
        if run_cmd(['udisksctl', 'mount', '-b', part]):
            mount_len += 1

    if mount_len:
        run_cmd(['notify-send', 'Mounted {} file-systems!'.format(mount_len)])
    else:
        run_cmd(['notify-send', 'Nothing to mount!'])


if __name__ == '__main__':
    if "--mount" in sys.argv:
        mount_all()
    elif "--unmount" in sys.argv:
        unmount_all(power_off=False)
    elif "--unmount-power-off" in sys.argv:
        unmount_all(power_off=True)
    else:
        print("Expected one of ['--mount', '--unmount' or '--unmount-power-off'] to be passed.")
    
por ideasman42 21.07.2017 / 09:34
0

Uma alternativa é usar o comando umount com: sudo umount / media / user / seu-usb-drive

Para determinar a unidade certa, você pode usar o comando mount: mount | grep usb

    
por kukulo 21.07.2017 / 07:12