input: Write a function to implement a FileSystem class in Python that provides methods to save, delete, and find task data. The class should be instantiated with a parameter dictionary containing a 'taskDir' key indicating the directory where task data should be stored. It should include methods for saving task data, deleting task data by task ID, finding task data by task ID, finding multiple task data by a list of task IDs, retrieving all task data, and getting task data for a specific ID without raising an error if not found. The functions should handle the creation and deletion of task directories and files, and manage task IDs.
  
  class FileSystem(object):
      def __init__(self, param):
          self.__jsub_dir = os.path.expanduser(param.get('taskDir', '~/jsub/'))
          self.__logger = logging.getLogger('JSUB')
          self.__json_format = param.get('format', 'compact')
  
      def save_task(self, data):
          if 'id' not in data:
              safe_mkdir(self.__jsub_dir)
              data['id'] = self.__new_task_id()
          safe_mkdir(os.path.join(self.__jsub_dir,str(data['id']),'taskInfo'))
          task_path = os.path.join(self.__jsub_dir, str(data['id']),'taskInfo','repo')
          data_str = self.__json_str(data)
          with open(task_path, 'a+') as f:
              fcntl.flock(f, fcntl.LOCK_EX)
              f.seek(0)
              f.truncate()
              f.write(data_str)
  
      def delete_task(self, task_id):
          safe_rmdir(os.path.join(self.__jsub_dir,str(task_id)))
  
      def find_by_id(self, task_id):
          return self.task_data(task_id)
  
      def find_by_ids(self, task_ids):
          all_data = []
          for task_id in task_ids:
              try:
                  td = self.task_data(task_id)
                  all_data.append(td)
              except RepoReadError as e:
                  self.__logger.debug(e)
          return all_data
  
      def all_task_data(self, order='asc'):
          task_ids =[d for d in os.listdir(self.__jsub_dir) if os.path.isdir(os.path.join(self.__jsub_dir,d))]
          task_ids.sort(key=int, reverse=(order=='desc'))
          return self.find_by_ids(task_ids)
  
      def task_data(self, task_id):
          safe_mkdir(os.path.join(self.__jsub_dir,str(task_id),'taskInfo'))
          task_path = os.path.join(self.__jsub_dir,str(task_id),'taskInfo','repo')
          with open(task_path, 'a+') as f:
              fcntl.flock(f, fcntl.LOCK_EX)
              f.seek(0)
              data_str = f.read()
          try:
              return json.loads(data_str)
          except ValueError as e:
              raise RepoReadError('JSON decode error on task %s: %s' % (task_id, e))
  
      def __new_task_id(self):
          task_ids =[int(d) for d in os.listdir(self.__jsub_dir) if os.path.isdir(os.path.join(self.__jsub_dir,d))]
          if not task_ids:
              return 1
          task_ids.sort(key=int, reverse=True)
          return(task_ids[0]+1)
  
      def __json_str(self, data):
          if self.__json_format == 'pretty':
              return json.dumps(data, indent=2)
          return json.dumps(data, separators=(',', ':'))
  
  assert FileSystem({'taskDir': '~/jsub/'}).save_task({'name': 'task1', 'data': 'some data'})['id'] == 1
  assert FileSystem({'taskDir': '~/jsub/'}).find_by_id(1)['name'] == 'task1'
  assert len(FileSystem({'taskDir': '~/jsub/'}).find_by_ids([1, 2])) == 1
  assert FileSystem({'taskDir': '~/jsub/'}).delete_task(1) is None
  assert FileSystem({'taskDir': '~/jsub/'}).task_data(1) is None
  assert len(FileSystem({'taskDir': '~/jsub/'}).all_task_data()) == 0
output: 
  def __init__(self, param):
      self.__jsub_dir = os.path.expanduser(param.get('taskDir', '~/jsub/'))
      self.__logger = logging.getLogger('JSUB')
      self.__json_format = param.get('format', 'compact')
  
  def save_task(self, data):
      if 'id' not in data:
          safe_mkdir(self.__jsub_dir)
          data['id'] = self.__new_task_id()
      safe_mkdir(os.path.join(self.__jsub_dir,str(data['id']),'taskInfo'))
      task_path = os.path.join(self.__jsub_dir, str(data['id']),'taskInfo','repo')
      data_str = self.__json_str(data)
      with open(task_path, 'a+') as f:
          fcntl.flock(f, fcntl.LOCK_EX)
          f.seek(0)
          f.truncate()
          f.write(data_str)
  
  def delete_task(self, task_id):
      safe_rmdir(os.path.join(self.__jsub_dir,str(task_id)))
  
  def find_by_id(self, task_id):
      return self.task_data(task_id)
  
  def find_by_ids(self, task_ids):
      all_data = []
      for task_id in task_ids:
          try:
              td = self.task_data(task_id)
              all_data.append(td)
          except RepoReadError as e:
              self.__logger.debug(e)
      return all_data
  
  def all_task_data(self, order='asc'):
      task_ids =[d for d in os.listdir(self.__jsub_dir) if os.path.isdir(os.path.join(self.__jsub_dir,d))]
      task_ids.sort(key=int, reverse=(order=='desc'))
      return self.find_by_ids(task_ids)
  
  def task_data(self, task_id):
      safe_mkdir(os.path.join(self.__jsub_dir,str(task_id),'taskInfo'))
      task_path = os.path.join(self.__jsub_dir,str(task_id),'taskInfo','repo')
      with open(task_path, 'a+') as f:
          fcntl.flock(f, fcntl.LOCK_EX)
          f.seek(0)
          data_str = f.read()
      try:
          return json.loads(data_str)
      except ValueError as e:
          raise RepoReadError('JSON decode error on task %s: %s' % (task_id, e))
  
  def __new_task_id(self):
      task_ids =[int(d) for d in os.listdir(self.__jsub_dir) if os.path.isdir(os.path.join(self.__jsub_dir,d))]
      if not task_ids:
          return 1
      task_ids.sort(key=int, reverse=True)
      return(task_ids[0]+1)
  
  def __json_str(self, data):
      if self.__json_format == 'pretty':
          return json.dumps(data, indent=2)
      return json.dumps(data, separators=(',', ':'))
  